表示内容
F# のリストは、順序が指定されており変更できない一連の同じ型の要素です。 リストに対して基本的な操作を実行するには、List モジュールの関数を使用します。
リストの作成と初期化
リストを定義するには、次のコード行に示すように、セミコロンで区切って明示的にリストした要素を角かっこで囲みます。
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 ]
空のリストは、間に何も含まない 1 組の角かっこで示します。
// An empty list.
let listEmpty = []
シーケンス式を使用してリストを作成することもできます。 詳細については、「シーケンス式」を参照してください。 たとえば、次のコードでは 1 から 10 までの整数の 2 乗のリストが作成されます。
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
リストに対して操作を実行する関数は、List モジュールにあります。
F# のリストは変更できないため、変更操作を行うと、既存のリストが変更されるのではなく、新しいリストが生成されます。
F# のリストは、シングル リンク リストとして実装されます。つまり、リストの先頭だけにアクセスする操作は O(1) で、要素へのアクセスは O(n) になります。
プロパティ
リスト型では次のプロパティがサポートされています。
プロパティ | タイプ | 説明 |
---|---|---|
Head | 'T |
1 番目の要素。 |
空 | 'T list |
該当する型の空のリストを返す静的プロパティ。 |
IsEmpty | bool |
リストに要素がない場合は true です。 |
Item | 'T |
指定したインデックスの要素 (起点を 0 とする)。 |
Length | int |
要素の数。 |
Tail | 'T list |
1 番目の要素を除いたリスト。 |
これらのプロパティを使用したいくつかの例を次に示します。
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
は、2 つのリストを受け取る再帰関数です。 1 番目のリストは、倍数を削除する数値が格納されたリストで、2 番目のリストは、数値を削除する元のリストです。 次の例のコードでは、この再帰関数を使用してリストから素数以外をすべて削除します。その結果、素数のリストが残ります。
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]
モジュール関数
List モジュールには、リストの要素にアクセスする関数があります。 先頭の要素には、最も迅速かつ簡単にアクセスできます。 プロパティ Head またはモジュール関数 List.head を使用します。 リストの後部にアクセスするには、Tail プロパティまたは List.tail 関数を使用します。 インデックスで要素を検索するには、List.nth 関数を使用します。 List.nth
はリストを走査します。 したがってこれは O(n) です。 コードで List.nth
を頻繁に使用する場合は、リストの代わりに配列を使用すると、効果的である可能性があります。 配列での要素のアクセスは O(1) です。
リストに対するブール演算
List.isEmpty 関数は、リストに要素があるかどうかを調べる関数です。
List.exists 関数では、リストの要素に対してブール値の評価が行われ、条件を満たす要素がある場合には true
が返されます。 List.exists2 も同様ですが、2 つのリストの一連の要素のペアを操作できます。
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 では、2 つのリストの対応する位置にあるすべての要素が、要素の各ペアに関係するブール式を満たすかどうかが調べられます。
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 です。 並べ替え関数は、これら 3 つの関数のどれを使用するかを判断します。 List.sort
は、既定の一般的な比較を使用します。 一般的な比較は、汎用の比較関数に基づくグローバル演算子を使用して、値を比較します。 この比較は、単純な数値型、タプル、レコード、判別共用体、リスト、配列、および System.IComparable
を実装する任意の型など、広範な要素型で効率的に動作します。 System.IComparable
を実装する型の場合は、汎用的な比較で System.IComparable.CompareTo()
関数が使用されます。 また、汎用的な比較は文字列にも使用できますが、カルチャに依存しない並べ替え順序が使用されます。 関数型のようなサポートされない型には、汎用的な比較を使用できません。 また、既定の汎用的な比較は、小さい構造の型の場合に最高のパフォーマンスを示します。比較と並べ替えが頻繁に必要な大きい構造の型の場合は、System.IComparable
を実装し、System.IComparable.CompareTo()
メソッドを効率的に実装することを考慮してください。
List.sortBy
関数は、並べ替え基準として使用される値を返す関数を受け取り、List.sortWith
関数は、比較関数を引数として受け取ります。 これら 2 つの関数は、比較をサポートしない型を使用するとき、またはカルチャを認識する文字列の場合のように複雑な比較セマンティクスを必要とする比較の場合に役立ちます。
次の例は、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
を使用して、まず、カスタム型の 1 つのフィールドを比較し、最初のフィールドの値が同じである場合は、さらに別のフィールドを比較しています。
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
The result is 5.
最初に要素を変換する必要がある場合は、List.pick を呼び出します。これは、オプションを返す関数を受け取り、Some(x)
である最初のオプション値を検索します。 List.pick
は要素を返す代わりに、結果 x
を返します。 一致する要素が見つからない場合、List.pick
は System.Collections.Generic.KeyNotFoundException
をスローします。 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"
もう 1 つの検索操作グループである 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 モジュールには、合計や平均などの一般的な算術演算が組み込まれています。 List.sum を使用するには、リストの要素の型が +
演算子をサポートし、ゼロ値を備えていることも必要です。 すべての組み込み数値型はこの条件を満たしています。 List.average を使用するには、剰余のない除算が要素の型によってサポートされている必要があります。そのため、整数型では使用できませんが、浮動小数点型では使用できます。 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 関数で操作できます。 これらの関数は、単一値の 2 つのリストを結合してタプルのリストを 1 つ生成したり、タプルの 1 つのリストを分割して単一の値のリストを 2 つ生成したりします。 最も単純な List.zip 関数では、単一の要素から成る 2 つのリストが受け取られ、タプルのペアで構成された 1 つのリストが生成されます。 もう 1 つのバージョンである List.zip3 では、単一の要素から成る 3 つのリストが受け取られ、3 つの要素を持つタプルで構成された 1 つのリストが生成されます。 次のコード例は、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 では、タプルのリストが受け取られ、タプルの形式のリストが返されます。ここで、第 1 のリストは、各タプルの 1 番目にあるすべての要素を含み、第 2 のリストは、各タプルの 2 番目の要素を含み、以降も同様に続きます。
次のコード例は、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 です。これを使用すると、リストのすべての要素に対する関数を呼び出すことができます。 バリエーションには、2 つのリストの要素に対して操作を実行できる List.iter2、各要素に対して呼び出された関数に引数として各要素のインデックスを渡すことを除いては List.iter
に似ている List.iteri、および List.iter2
と List.iteri
の機能を組み合わせた 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
また、リストの要素の変換によく使用されるもう 1 つの関数 List.map では、リストの各要素に関数を適用し、すべての結果を含む新しいリストを生成できます。 List.map2 と List.map3 は、複数のリストを受け取るバリエーションです。 また、要素に加えて、各要素のインデックスを関数に渡す必要がある場合は、List.mapi および List.mapi2 も使用できます。 List.mapi2
と List.mapi
の唯一の違いは、List.mapi2
では 2 つのリストが使用される点です。 次の例に 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 は、各要素が生成するリストがすべて連結されて最終的に 1 つのリストになる点を除き、List.map
と似ています。 次のコードでは、リストの各要素が 3 つの値を生成します。 これらすべてが 1 つのリストに集約されます。
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]
です。
map と filter を組み合わせた 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"]
複数のリストに対する操作
複数のリストを結合できます。 2 つのリストを 1 つに結合するには、List.append を使用します。 3 つ以上のリストを結合するには、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
に似ていますが、これらの操作には、計算時の情報を保持する accumulator という名前の追加のパラメーターがあります。
リストに対して計算を実行するには、List.fold
を使用します。
次のコード例は、List.fold を使用してさまざまな操作を実行する方法を示します。
リストが走査されます。アキュムレータ acc
は、計算の進行に伴って渡される値です。 1 番目の引数はアキュムレータとリスト要素を受け取り、そのリスト要素に対する計算の中間結果を返します。 2 番目の引数はアキュムレータの初期値です。
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 は、2 つのリストに対して計算を実行します。
次の例は、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.fold
が追加のパラメーターの最終値を返すのに対し、List.scan
は (最終値に加え) 追加のパラメーターの中間値のリストを返すことです。
これらの各関数には、リストの走査順序と引数の順序が逆であるバリエーションがあります (List.foldBack など)。 また、List.fold
と List.foldBack
にはそれぞれ、同じ長さの 2 つのリストを受け取る List.fold2 および List.foldBack2 というバリエーションがあります。 各要素に対して実行される関数では、両方のリストの対応する要素を使用して操作を実行できます。 2 つのリストの要素の型が同じである必要はありません。たとえば、次の例では、一方のリストには銀行口座の取引金額が格納され、もう一方のリストには取引の種類 (預け入れまたは引き出し) が格納されています。
// 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.fold
や List.scan
に似ています。ただし、List.reduce
では、別のアキュムレータが受け渡されるのではなく、関数が受け取られます。この関数は要素の型の引数を、1 つだけでなく、2 つ受け取り、この引数の 1 つがアキュムレータとして機能して、計算の途中結果を保持します。 List.reduce
は、初めに最初の 2 つのリスト要素に対して演算を実行し、次にその演算の結果と次の要素を合わせて使用します。 独自の型を持つ別のアキュムレータがないため、List.reduce
を List.fold
の代わりに使用できるのは、アキュムレータと要素が同じ型を持つ場合だけです。 List.reduce
を使用したコードの例を次に示します。 指定されたリストに要素がない場合、List.reduce
は例外をスローします。
次のコードでは、ラムダ式の最初の呼び出しで引数 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 を使用します。
その他の操作
リストに対するその他の操作については、ライブラリ リファレンスのトピック「List モジュール」を参照してください。
関連項目
.NET