목록
F#의 목록은 순서가 지정되고 변경할 수 없는 일련의 동일 형식 요소입니다. 목록에 대한 기본 작업을 수행하려면 목록 모듈의 함수를 사용합니다.
목록 만들기 및 초기화
아래 코드 줄에 나와 있는 것처럼 세미콜론으로 구분되고 대괄호로 묶인 요소를 명시적으로 목록 처리하여 목록을 정의할 수 있습니다.
let list123 = [ 1; 2; 3 ]
요소 간에 줄 바꿈을 삽입하고 필요에 따라 세미콜론을 사용할 수도 있습니다. 요소 초기화 식이 더 길거나 각 요소에 대해 주석을 추가하려는 경우에는 이 두 번째 구문을 사용하면 코드를 더 쉽게 읽을 수 있습니다.
let list123 = [ 1; 2; 3 ]
일반적으로 모든 목록 요소는 같은 형식이어야 합니다. 단, 요소가 기본 형식으로 지정된 목록에는 파생 형식 요소를 포함할 수 있습니다. 그러므로 Button
및 CheckBox
가 모두 Control
에서 파생되는 아래 코드는 정상적으로 사용 가능합니다.
let myControlList: Control list = [ new Button(); new CheckBox() ]
아래 코드에 나와 있는 것처럼 범위 연산자(..
)로 구분된 정수로 표시되는 범위를 사용하여 목록 요소를 정의할 수도 있습니다.
let list1 = [ 1..10 ]
빈 목록은 안에 아무 내용이 없는 대괄호 쌍으로 지정됩니다.
// An empty list.
let listEmpty = []
시퀀스 식을 사용하여 목록을 만들 수도 있습니다. 자세한 내용은 시퀀스 식을 참조하세요. 예를 들어 다음 코드는 정수 1~10의 제곱 목록을 만듭니다.
let listOfSquares = [ for i in 1..10 -> i * i ]
목록 사용을 위한 연산자
::
(cons) 연산자를 사용하여 목록에 요소를 연결할 수 있습니다. list1
이 [2; 3; 4]
이면 다음 코드는 list2
를 [100; 2; 3; 4]
로 작성합니다.
let list2 = 100 :: list1
다음 코드와 같이 @
연산자를 사용하여 호환 형식을 포함하는 목록을 연결할 수 있습니다. list1
이 [2; 3; 4]
이고 list2
가 [100; 2; 3; 4]
이면 다음 코드는 list3
을 [2; 3; 4; 100; 2; 3; 4]
로 작성합니다.
let list3 = list1 @ list2
목록에 대한 작업을 수행하기 위한 함수는 목록 모듈에서 사용할 수 있습니다.
F#의 목록은 변경이 불가능하므로 수정 작업을 수행하면 기존 목록이 수정되는 대신 새 목록이 생성됩니다.
F#의 목록은 Singly 연결된 목록으로 구현됩니다. 즉, 목록의 헤드에만 액세스하는 작업은 O(1)이고 요소 액세스는 O(n)입니다.
속성
목록 형식이 지원하는 속성은 다음과 같습니다.
속성 | Type | 설명 |
---|---|---|
머리 | 'T |
첫 번째 요소입니다. |
Empty | 'T list |
해당 형식의 빈 목록을 반환하는 정적 속성입니다. |
IsEmpty | bool |
목록에 요소가 없으면 true 입니다. |
항목 | 'T |
지정한 인덱스에 있는 요소로서 0부터 시작합니다. |
길이 | int |
요소의 수입니다. |
꼬리 | 'T list |
첫 번째 요소가 없는 목록입니다. |
이러한 속성을 사용하는 몇 가지 예는 다음과 같습니다.
let list1 = [ 1; 2; 3 ]
// Properties
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))
목록 사용
목록을 사용하여 프로그래밍할 때는 적은 양의 코드만으로도 복잡한 작업을 수행할 수 있습니다. 이 섹션에서는 기능적인 프로그래밍에 중요한 일반적인 목록 관련 작업에 대해 설명합니다.
목록을 사용한 재귀
목록을 고유한 방식으로 재귀 프로그래밍 기술에 맞게 사용할 수 있습니다. 목록의 모든 요소에 대해 수행해야 하는 작업을 고려해 보세요. 목록 헤드에 대해 작업을 수행한 다음 목록 꼬리(첫 번째 요소를 제외한 원래 목록으로 구성되는 더 작은 목록)를 다음 재귀 수준으로 다시 전달하는 재귀적인 방식으로 이 작업을 수행할 수 있습니다.
이러한 재귀 함수를 작성하려면 패턴 일치에서 cons 연산자(::
)를 사용합니다. 그러면 목록 헤드를 꼬리에서 분리할 수 있습니다.
다음 코드 예제에서는 패턴 일치를 사용하여 목록에 대해 작업을 수행하는 재귀 함수를 구현하는 방법을 보여줍니다.
let rec sum list =
match list with
| head :: tail -> head + sum tail
| [] -> 0
위의 코드는 작은 목록에서는 효율적으로 작동하지만 큰 목록에서는 스택이 오버플로될 수 있습니다. 위의 코드보다 개선된 다음 코드에서는 재귀 함수 사용을 위한 표준 기술인 누적기 인수를 사용합니다. 누적기 인수를 사용하면 함수 꼬리가 재귀적으로 적용되므로 스택 공간이 절약됩니다.
let sum list =
let rec loop list acc =
match list with
| head :: tail -> loop tail (acc + head)
| [] -> acc
loop list 0
RemoveAllMultiples
함수는 목록 두 개를 사용하는 재귀 함수입니다. 첫 번째 목록은 배수를 제거할 숫자를 포함하며 두 번째 목록은 숫자를 제거할 목록입니다. 다음 예의 코드에서는 이 재귀 함수를 사용하여 목록에서 소수가 아닌 숫자를 모두 삭제해 소수만 포함된 목록을 생성합니다.
let IsPrimeMultipleTest n x = x = n || x % n <> 0
let rec RemoveAllMultiples listn listx =
match listn with
| head :: tail -> RemoveAllMultiples tail (List.filter (IsPrimeMultipleTest head) listx)
| [] -> listx
let GetPrimesUpTo n =
let max = int (sqrt (float n))
RemoveAllMultiples [ 2..max ] [ 1..n ]
printfn "Primes Up To %d:\n %A" 100 (GetPrimesUpTo 100)
출력은 다음과 같습니다.
Primes Up To 100:
[2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47; 53; 59; 61; 67; 71; 73; 79; 83; 89; 97]
모듈 함수
목록 모듈은 목록의 요소에 액세스하는 함수를 제공합니다. 가장 빠르고 쉽게 액세스할 수 있는 요소는 헤드 요소입니다. Head 또는 모듈 함수 List.head 속성을 사용합니다. Tail 속성 또는 List.tail 함수를 사용하여 목록의 꼬리에 액세스할 수 있습니다. 인덱스별로 요소를 찾으려면 List.nth 함수를 사용합니다. List.nth
는 목록을 트래버스하므로 따라서 O(n)입니다. 코드에서 List.nth
를 자주 사용하는 경우 목록 대신 배열을 사용해볼 수 있습니다. 배열의 요소 액세스는 O(1)입니다.
목록에 대한 부울 작업
List.isEmpty 함수는 목록에 요소가 있는지 여부를 결정합니다.
List.exists 함수는 부울 테스트를 목록의 요소에 적용하고 테스트에 맞는 요소가 있으면 반환 true
합니다. List.exists2 는 비슷하지만 두 목록의 연속 요소 쌍에서 작동합니다.
다음 코드는 List.exists
의 사용법을 보여줍니다.
// Use List.exists to determine whether there is an element of a list satisfies a given Boolean expression.
// containsNumber returns true if any of the elements of the supplied list match
// the supplied number.
let containsNumber number list = List.exists (fun elem -> elem = number) list
let list0to3 = [0 .. 3]
printfn "For list %A, contains zero is %b" list0to3 (containsNumber 0 list0to3)
출력은 다음과 같습니다.
For list [0; 1; 2; 3], contains zero is true
다음 예에서는 List.exists2
의 사용법을 보여줍니다.
// Use List.exists2 to compare elements in two lists.
// isEqualElement returns true if any elements at the same position in two supplied
// lists match.
let isEqualElement list1 list2 = List.exists2 (fun elem1 elem2 -> elem1 = elem2) list1 list2
let list1to5 = [ 1 .. 5 ]
let list5to1 = [ 5 .. -1 .. 1 ]
if (isEqualElement list1to5 list5to1) then
printfn "Lists %A and %A have at least one equal element at the same position." list1to5 list5to1
else
printfn "Lists %A and %A do not have an equal element at the same position." list1to5 list5to1
출력은 다음과 같습니다.
Lists [1; 2; 3; 4; 5] and [5; 4; 3; 2; 1] have at least one equal element at the same position.
목록의 모든 요소가 조건을 충족하는지 테스트하려는 경우 List.forall을 사용할 수 있습니다.
let isAllZeroes list = List.forall (fun elem -> elem = 0.0) list
printfn "%b" (isAllZeroes [0.0; 0.0])
printfn "%b" (isAllZeroes [0.0; 1.0])
출력은 다음과 같습니다.
true
false
마찬가지로 List.forall2 는 두 목록의 해당 위치에 있는 모든 요소가 각 요소 쌍을 포함하는 부울 식을 충족하는지 여부를 결정합니다.
let listEqual list1 list2 = List.forall2 (fun elem1 elem2 -> elem1 = elem2) list1 list2
printfn "%b" (listEqual [0; 1; 2] [0; 1; 2])
printfn "%b" (listEqual [0; 0; 0] [0; 1; 0])
출력은 다음과 같습니다.
true
false
목록에 대한 정렬 작업
List.sort, List.sortBy 및 List.sortWith 함수는 목록을 정렬합니다. 정렬 함수는 이 세 함수 중 사용할 함수를 결정합니다. List.sort
는 기본 제네릭 비교를 사용합니다. 제네릭 비교에서는 제네릭 비교 함수를 기반으로 전역 연산자를 사용해 값을 비교합니다. 이 함수는 단순한 숫자 형식, 튜플, 레코드, 구분된 공용 구조체, 목록, 배열, 그리고 System.IComparable
을 구현하는 모든 형식과 같은 광범위한 요소 형식에서 효율적으로 작동합니다. System.IComparable
을 구현하는 형식의 경우 제네릭 비교에서는 System.IComparable.CompareTo()
함수를 사용합니다. 제네릭 비교는 문자열에도 작동하지만 이 경우에는 문화권과 독립적인 정렬 순서를 사용합니다. 함수 형식 등의 지원되지 않는 형식에는 제네릭 비교를 사용하면 안 됩니다. 또한 기본 제네릭 비교의 성능은 작은 구조의 형식에서 가장 우수합니다. 자주 비교하고 정렬해야 하는 큰 구조의 형식에 대해서는 System.IComparable
을 구현하고 효율적인 System.IComparable.CompareTo()
메서드 구현을 제공하는 것이 좋습니다.
List.sortBy
는 정렬 기준으로 사용되는 값을 반환하는 함수를 사용하며 List.sortWith
는 비교 함수를 인수로 사용합니다. 비교를 지원하지 않는 형식으로 작업 중이거나 비교 시 문화권 인식 문자열과 같이 보다 복잡한 비교 의미 체계를 사용할 때는 이 두 함수가 유용합니다.
다음 예에서는 List.sort
의 사용법을 보여줍니다.
let sortedList1 = List.sort [1; 4; 8; -2; 5]
printfn "%A" sortedList1
출력은 다음과 같습니다.
[-2; 1; 4; 5; 8]
다음 예에서는 List.sortBy
의 사용법을 보여줍니다.
let sortedList2 = List.sortBy (fun elem -> abs elem) [1; 4; 8; -2; 5]
printfn "%A" sortedList2
출력은 다음과 같습니다.
[1; -2; 4; 5; 8]
다음 예에서는 List.sortWith
의 사용법을 보여줍니다. 이 예에서는 사용자 지정 비교 함수 compareWidgets
를 사용하여 먼저 사용자 지정 형식의 한 필드를 비교한 다음 첫 필드의 값이 같으면 다른 필드를 비교합니다.
type Widget = { ID: int; Rev: int }
let compareWidgets widget1 widget2 =
if widget1.ID < widget2.ID then -1 else
if widget1.ID > widget2.ID then 1 else
if widget1.Rev < widget2.Rev then -1 else
if widget1.Rev > widget2.Rev then 1 else
0
let listToCompare = [
{ ID = 92; Rev = 1 }
{ ID = 110; Rev = 1 }
{ ID = 100; Rev = 5 }
{ ID = 100; Rev = 2 }
{ ID = 92; Rev = 1 }
]
let sortedWidgetList = List.sortWith compareWidgets listToCompare
printfn "%A" sortedWidgetList
출력은 다음과 같습니다.
[{ID = 92;
Rev = 1;}; {ID = 92;
Rev = 1;}; {ID = 100;
Rev = 2;}; {ID = 100;
Rev = 5;}; {ID = 110;
Rev = 1;}]
목록에 대한 검색 작업
목록에는 다양한 검색 작업이 지원됩니다. 가장 간단한 List.find를 사용하면 지정된 조건과 일치하는 첫 번째 요소를 찾을 수 있습니다.
다음 코드 예제에서는 List.find
를 사용하여 목록에서 5로 나눌 수 있는 첫 번째 숫자를 찾습니다.
let isDivisibleBy number elem = elem % number = 0
let result = List.find (isDivisibleBy 5) [ 1 .. 100 ]
printfn "%d " result
해당 결과는 5입니다.
요소를 먼저 변환해야 하는 경우 List.pick을 호출 합니다. List.pick은 옵션을 반환하는 함수를 사용하고 첫 번째 옵션 값을 찾습니다 Some(x)
. List.pick
은 요소를 반환하는 대신 x
결과를 반환합니다. 일치하는 요소가 없으면 List.pick
은 System.Collections.Generic.KeyNotFoundException
을 throw합니다. 다음 코드는 List.pick
의 사용법을 보여줍니다.
let valuesList = [ ("a", 1); ("b", 2); ("c", 3) ]
let resultPick = List.pick (fun elem ->
match elem with
| (value, 2) -> Some value
| _ -> None) valuesList
printfn "%A" resultPick
출력은 다음과 같습니다.
"b"
다른 검색 작업 그룹인 List.tryFind 및 관련 함수는 옵션 값을 반환합니다. List.tryFind
함수는 조건을 만족하는 목록의 첫 번째 요소가 있으면 해당 요소를 반환하고 요소가 없으면 옵션 값 None
을 반환합니다. 변형 List.tryFindIndex 는 요소 자체가 아닌 요소의 인덱스를 반환합니다. 다음 코드에서는 이러한 함수를 보여줍니다.
let list1d = [1; 3; 7; 9; 11; 13; 15; 19; 22; 29; 36]
let isEven x = x % 2 = 0
match List.tryFind isEven list1d with
| Some value -> printfn "The first even value is %d." value
| None -> printfn "There is no even value in the list."
match List.tryFindIndex isEven list1d with
| Some value -> printfn "The first even value is at position %d." value
| None -> printfn "There is no even value in the list."
출력은 다음과 같습니다.
The first even value is 22.
The first even value is at position 8.
목록에 대한 산술 연산
합계 및 평균과 같은 일반적인 산술 연산은 목록 모듈에 기본 제공됩니다. List.sum을 사용하려면 list 요소 형식이 연산자를 +
지원하고 값이 0이어야 합니다. 모든 기본 제공 연산 형식은 이러한 조건을 만족합니다. List.average를 사용하려면 요소 형식이 정수 계열 형식을 제외하지만 부동 소수점 형식을 허용하는 re기본der 없이 나누기를 지원해야 합니다. List.sumBy 및 List.averageBy 함수는 함수를 매개 변수로 사용하며, 이 함수의 결과는 합계 또는 평균 값을 계산하는 데 사용됩니다.
다음 코드는 List.sum
, List.sumBy
및 List.average
의 사용법을 보여줍니다.
// Compute the sum of the first 10 integers by using List.sum.
let sum1 = List.sum [1 .. 10]
// Compute the sum of the squares of the elements of a list by using List.sumBy.
let sum2 = List.sumBy (fun elem -> elem*elem) [1 .. 10]
// Compute the average of the elements of a list by using List.average.
let avg1 = List.average [0.0; 1.0; 1.0; 2.0]
printfn "%f" avg1
1.000000
가 출력됩니다.
다음 코드는 List.averageBy
의 사용법을 보여줍니다.
let avg2 = List.averageBy (fun elem -> float elem) [1 .. 10]
printfn "%f" avg2
5.5
가 출력됩니다.
목록 및 튜플
튜플을 포함하는 목록은 zip 및 unzip 함수를 통해 조작할 수 있습니다. 이러한 함수는 단일 값의 두 목록을 튜플 목록 하나로 결합하거나 튜플 목록 하나를 단일 값의 두 목록으로 분리합니다. 가장 간단한 List.zip 함수는 두 개의 단일 요소 목록을 사용하고 단일 튜플 쌍 목록을 생성합니다. 또 다른 버전인 List.zip3은 단일 요소의 세 목록을 사용하고 세 개의 요소가 있는 단일 튜플 목록을 생성합니다. 다음 코드 예제에서는 List.zip
의 사용법을 보여줍니다.
let list1 = [ 1; 2; 3 ]
let list2 = [ -1; -2; -3 ]
let listZip = List.zip list1 list2
printfn "%A" listZip
출력은 다음과 같습니다.
[(1, -1); (2, -2); (3; -3)]
다음 코드 예제에서는 List.zip3
의 사용법을 보여줍니다.
let list3 = [ 0; 0; 0]
let listZip3 = List.zip3 list1 list2 list3
printfn "%A" listZip3
출력은 다음과 같습니다.
[(1, -1, 0); (2, -2, 0); (3, -3, 0)]
해당 unzip 버전인 List.unzip 및 List.unzip3은 튜플 목록을 가져와서 튜플에서 목록을 반환합니다. 여기서 첫 번째 목록에는 각 튜플의 첫 번째 요소가 모두 포함되고 두 번째 목록에는 각 튜플의 두 번째 요소가 포함됩니다.
다음 코드 예제에서는 List.unzip의 사용을 보여 줍니다.
let lists = List.unzip [(1,2); (3,4)]
printfn "%A" lists
printfn "%A %A" (fst lists) (snd lists)
출력은 다음과 같습니다.
([1; 3], [2; 4])
[1; 3] [2; 4]
다음 코드 예제에서는 List.unzip3의 사용을 보여 줍니다.
let listsUnzip3 = List.unzip3 [(1,2,3); (4,5,6)]
printfn "%A" listsUnzip3
출력은 다음과 같습니다.
([1; 4], [2; 5], [3; 6])
목록 요소에 대한 작업
F#에서는 목록 요소에 대한 여러 작업을 지원합니다. 가장 간단한 것은 목록의 모든 요소에서 함수를 호출할 수 있는 List.iter입니다. 변형에는 각 요소의 인덱스가 각 요소에 대해 호출되는 함수에 인수로 전달된다는 점을 제외하고 List.iteri라는 두 목록의 요소에 대해 작업을 수행할 수 있는 List.iter2List.iter
와 기능 List.iteri
List.iter2
의 조합인 List.iteri2가 포함됩니다. 다음 코드 예제에서는 이러한 함수를 보여줍니다.
let list1 = [1; 2; 3]
let list2 = [4; 5; 6]
List.iter (fun x -> printfn "List.iter: element is %d" x) list1
List.iteri(fun i x -> printfn "List.iteri: element %d is %d" i x) list1
List.iter2 (fun x y -> printfn "List.iter2: elements are %d %d" x y) list1 list2
List.iteri2 (fun i x y ->
printfn "List.iteri2: element %d of list1 is %d element %d of list2 is %d"
i x i y)
list1 list2
출력은 다음과 같습니다.
List.iter: element is 1
List.iter: element is 2
List.iter: element is 3
List.iteri: element 0 is 1
List.iteri: element 1 is 2
List.iteri: element 2 is 3
List.iter2: elements are 1 4
List.iter2: elements are 2 5
List.iter2: elements are 3 6
List.iteri2: element 0 of list1 is 1; element 0 of list2 is 4
List.iteri2: element 1 of list1 is 2; element 1 of list2 is 5
List.iteri2: element 2 of list1 is 3; element 2 of list2 is 6
목록 요소를 변환하는 또 다른 자주 사용되는 함수는 목록의 각 요소에 함수를 적용하고 모든 결과를 새 목록에 넣을 수 있는 List.map입니다. List.map2 및 List.map3 은 여러 목록을 사용하는 변형입니다. 또한 요소 외에도 함수를 각 요소의 인덱스로 전달해야 하는 경우 List.mapi 및 List.mapi2를 사용할 수 있습니다. List.mapi2
와 List.mapi
의 차이점은 List.mapi2
의 경우 목록 두 개를 사용한다는 것뿐입니다. 다음 예제에서는 List.map을 보여 줍니다.
let list1 = [1; 2; 3]
let newList = List.map (fun x -> x + 1) list1
printfn "%A" newList
출력은 다음과 같습니다.
[2; 3; 4]
다음 예제에서는 List.map2
의 사용을 보여 줍니다.
let list1 = [1; 2; 3]
let list2 = [4; 5; 6]
let sumList = List.map2 (fun x y -> x + y) list1 list2
printfn "%A" sumList
출력은 다음과 같습니다.
[5; 7; 9]
다음 예제에서는 List.map3
의 사용을 보여 줍니다.
let newList2 = List.map3 (fun x y z -> x + y + z) list1 list2 [2; 3; 4]
printfn "%A" newList2
출력은 다음과 같습니다.
[7; 10; 13]
다음 예제에서는 List.mapi
의 사용을 보여 줍니다.
let newListAddIndex = List.mapi (fun i x -> x + i) list1
printfn "%A" newListAddIndex
출력은 다음과 같습니다.
[1; 3; 5]
다음 예제에서는 List.mapi2
의 사용을 보여 줍니다.
let listAddTimesIndex = List.mapi2 (fun i x y -> (x + y) * i) list1 list2
printfn "%A" listAddTimesIndex
출력은 다음과 같습니다.
[0; 7; 18]
List.collect 는 각 요소가 목록을 생성하고 이러한 모든 목록이 최종 목록에 연결된다는 점을 제외하면 비슷합니다 List.map
. 다음 코드에서는 목록의 각 요소가 숫자 3개를 생성합니다. 이러한 숫자는 모두 목록 하나에 수집됩니다.
let collectList = List.collect (fun x -> [for i in 1..3 -> x * i]) list1
printfn "%A" collectList
출력은 다음과 같습니다.
[1; 2; 3; 2; 4; 6; 3; 6; 9]
또한 부울 조건을 사용하고 지정된 조건을 충족하는 요소로만 구성된 새 목록을 생성하는 List.filter를 사용할 수도 있습니다.
let evenOnlyList = List.filter (fun x -> x % 2 = 0) [1; 2; 3; 4; 5; 6]
그러면 [2; 4; 6]
목록이 생성됩니다.
맵과 필터의 조합인 List.choose 을 사용하면 요소를 동시에 변환하고 선택할 수 있습니다. List.choose
는 목록의 각 요소에 대한 옵션을 반환하는 함수를 적용하고, 함수가 옵션 값 Some
을 반환하면 요소에 대해 새 결과 목록을 반환합니다.
다음 코드는 List.choose
를 사용하여 단어 목록에서 대문자로 표기된 단어를 선택하는 방법을 보여줍니다.
let listWords = [ "and"; "Rome"; "Bob"; "apple"; "zebra" ]
let isCapitalized (string1:string) = System.Char.IsUpper string1[0]
let results = List.choose (fun elem ->
match elem with
| elem when isCapitalized elem -> Some(elem + "'s")
| _ -> None) listWords
printfn "%A" results
출력은 다음과 같습니다.
["Rome's"; "Bob's"]
여러 목록에 대한 작업
여러 목록을 연결할 수 있습니다. 두 목록을 하나로 조인하려면 List.append를 사용합니다. 두 개 이상의 목록을 조인하려면 List.concat을 사용합니다.
let list1to10 = List.append [1; 2; 3] [4; 5; 6; 7; 8; 9; 10]
let listResult = List.concat [ [1; 2; 3]; [4; 5; 6]; [7; 8; 9] ]
List.iter (fun elem -> printf "%d " elem) list1to10
printfn ""
List.iter (fun elem -> printf "%d " elem) listResult
접기 및 검사 작업
모든 목록 요소가 상호 종속되어야 하는 목록 작업도 있습니다. 접기 및 스캔 작업은 각 요소에 대해 함수를 호출하는 것과 비슷하 List.iter
List.map
지만 이러한 작업은 계산을 통해 정보를 전달하는 누적기라는 추가 매개 변수를 제공합니다.
목록에 대해 계산을 수행하려면 List.fold
를 사용합니다.
다음 코드 예제에서는 List.fold를 사용하여 다양한 작업을 수행하는 방법을 보여 줍니다.
이 함수는 목록을 트래버스합니다. 누적기 acc
는 계산이 진행되면서 전달되는 값입니다. 첫 번째 인수는 누적기와 목록 요소를 사용하여 해당 목록 요소에 대한 계산의 중간 결과를 반환합니다. 두 번째 인수는 누적기의 초기 값입니다.
let sumList list = List.fold (fun acc elem -> acc + elem) 0 list
printfn "Sum of the elements of list %A is %d." [ 1 .. 3 ] (sumList [ 1 .. 3 ])
// The following example computes the average of a list.
let averageList list = (List.fold (fun acc elem -> acc + float elem) 0.0 list / float list.Length)
// The following example computes the standard deviation of a list.
// The standard deviation is computed by taking the square root of the
// sum of the variances, which are the differences between each value
// and the average.
let stdDevList list =
let avg = averageList list
sqrt (List.fold (fun acc elem -> acc + (float elem - avg) ** 2.0 ) 0.0 list / float list.Length)
let testList listTest =
printfn "List %A average: %f stddev: %f" listTest (averageList listTest) (stdDevList listTest)
testList [1; 1; 1]
testList [1; 2; 1]
testList [1; 2; 3]
// List.fold is the same as to List.iter when the accumulator is not used.
let printList list = List.fold (fun acc elem -> printfn "%A" elem) () list
printList [0.0; 1.0; 2.5; 5.1 ]
// The following example uses List.fold to reverse a list.
// The accumulator starts out as the empty list, and the function uses the cons operator
// to add each successive element to the head of the accumulator list, resulting in a
// reversed form of the list.
let reverseList list = List.fold (fun acc elem -> elem::acc) [] list
printfn "%A" (reverseList [1 .. 10])
이러한 함수 중 함수 이름에 숫자가 있는 버전은 둘 이상의 목록에 대해 작동합니다. 예를 들어 List.fold2 는 두 목록에 대해 계산을 수행합니다.
다음 예에서는 List.fold2
의 사용법을 보여줍니다.
// Use List.fold2 to perform computations over two lists (of equal size) at the same time.
// Example: Sum the greater element at each list position.
let sumGreatest list1 list2 = List.fold2 (fun acc elem1 elem2 ->
acc + max elem1 elem2) 0 list1 list2
let sum = sumGreatest [1; 2; 3] [3; 2; 1]
printfn "The sum of the greater of each pair of elements in the two lists is %d." sum
List.fold
및 List.scan은 추가 매개 변수의 최종 값을 반환하지만 List.scan
추가 매개 변수의 중간 값 목록(최종 값과 함께)을 반환한다는 List.fold
점에서 다릅니다.
이러한 각 함수에는 목록이 트래버스되는 순서와 인수 순서가 다른 역방향 변형(예 : List.foldBack)이 포함됩니다. List.fold
List.foldBack
또한 길이가 같은 두 목록을 사용하는 List.fold2 및 List.foldBack2 변형도 있습니다. 각 요소에 대해 실행되는 함수는 두 목록의 해당 요소를 사용하여 일부 작업을 수행할 수 있습니다. 다음 예에서와 같이 두 목록의 요소 형식은 다를 수 있습니다. 여기서 한 목록은 은행 계좌의 거래 금액을 포함하고 다른 목록은 거래 형식(입금 또는 출금)을 포함합니다.
// Discriminated union type that encodes the transaction type.
type Transaction =
| Deposit
| Withdrawal
let transactionTypes = [Deposit; Deposit; Withdrawal]
let transactionAmounts = [100.00; 1000.00; 95.00 ]
let initialBalance = 200.00
// Use fold2 to perform a calculation on the list to update the account balance.
let endingBalance = List.fold2 (fun acc elem1 elem2 ->
match elem1 with
| Deposit -> acc + elem2
| Withdrawal -> acc - elem2)
initialBalance
transactionTypes
transactionAmounts
printfn "%f" endingBalance
합계와 같은 계산의 경우 List.fold
및 List.foldBack
을 사용해도 결과는 동일합니다. 트래버스 순서에 따라 결과가 달라지지 않기 때문입니다. 다음 예에서는 List.foldBack
을 사용하여 목록에 요소를 추가합니다.
let sumListBack list = List.foldBack (fun elem acc -> acc + elem) list 0
printfn "%d" (sumListBack [1; 2; 3])
// For a calculation in which the order of traversal is important, fold and foldBack have different
// results. For example, replacing fold with foldBack in the listReverse function
// produces a function that copies the list, rather than reversing it.
let copyList list = List.foldBack (fun elem acc -> elem::acc) list []
printfn "%A" (copyList [1 .. 10])
다음 예에서는 은행 계좌 예로 다시 돌아옵니다. 이번에는 새로운 거래 형식인 이자 계산을 추가합니다. 따라서 최종 잔액은 거래 순서에 따라 달라집니다.
type Transaction2 =
| Deposit
| Withdrawal
| Interest
let transactionTypes2 = [Deposit; Deposit; Withdrawal; Interest]
let transactionAmounts2 = [100.00; 1000.00; 95.00; 0.05 / 12.0 ]
let initialBalance2 = 200.00
// Because fold2 processes the lists by starting at the head element,
// the interest is calculated last, on the balance of 1205.00.
let endingBalance2 = List.fold2 (fun acc elem1 elem2 ->
match elem1 with
| Deposit -> acc + elem2
| Withdrawal -> acc - elem2
| Interest -> acc * (1.0 + elem2))
initialBalance2
transactionTypes2
transactionAmounts2
printfn "%f" endingBalance2
// Because foldBack2 processes the lists by starting at end of the list,
// the interest is calculated first, on the balance of only 200.00.
let endingBalance3 = List.foldBack2 (fun elem1 elem2 acc ->
match elem1 with
| Deposit -> acc + elem2
| Withdrawal -> acc - elem2
| Interest -> acc * (1.0 + elem2))
transactionTypes2
transactionAmounts2
initialBalance2
printfn "%f" endingBalance3
List.reduce 함수는 별개의 누적기를 List.reduce
전달하는 대신 요소 형식의 두 인수를 사용하는 함수를 사용하고 이러한 인수 중 하나가 누적기 역할을 하므로 계산의 중간 결과를 저장한다는 점을 제외하면 다소 유사 List.fold
List.scan
합니다. List.reduce
는 먼저 처음 두 목록 요소에 대해 실행된 후 해당 작업의 결과를 다음 요소와 함께 사용합니다. 고유한 형식이 지정된 별도의 누적기가 없으므로 List.reduce
는 누적기와 요소 형식이 같을 때만 List.fold
대신 사용할 수 있습니다. 다음 코드는 List.reduce
의 사용법을 보여줍니다. 제공된 목록에 요소가 없으면 List.reduce
는 예외를 throw합니다.
다음 코드에서는 첫 번째 람다 식 호출에 인수 2와 4가 사용되며 결과로 6이 반환됩니다. 그 다음 호출에는 인수 6과 10이 사용되며 결과로 16이 반환됩니다.
let sumAList list =
try
List.reduce (fun acc elem -> acc + elem) list
with
| :? System.ArgumentException as exc -> 0
let resultSum = sumAList [2; 4; 10]
printfn "%d " resultSum
목록과 기타 컬렉션 형식 간 변환
List
모듈은 시퀀스와 배열 간 변환을 위한 함수를 제공합니다. 시퀀스로 또는 시퀀스에서 변환하려면 List.toSeq 또는 List.ofSeq를 사용합니다. 배열로 또는 배열에서 변환하려면 List.toArray 또는 List.ofArray를 사용합니다.
추가 작업
목록에 대한 추가 작업에 대한 자세한 내용은 라이브러리 참조 항목 목록 모듈을 참조하세요.
참고 항목
.NET