配列 (F#)
配列は、0 から始まる一連のデータ要素の、固定サイズの変更可能なコレクションで、その型はすべて同じです。
配列の作成
配列は複数の方法で作成できます。 小さなサイズの配列は、[| と |] の間に、連続する値をセミコロンで区切って列記して作成できます。次に例を示します。
let array1 = [| 1; 2; 3 |]
各要素を別々の行に配置することもでき、その場合は、セミコロンの区切り記号を省略できます。
let array1 =
[|
1
2
3
|]
配列の要素の型は、使用されるリテラルから推論され、すべて同じである必要があります。 次のコードは、1.0 が浮動小数点数で、2 と 3 は整数であるため、エラーになります。
// Causes an error.
// let array2 = [| 1.0; 2; 3 |]
シーケンス式を使用して配列を作成することもできます。 1 から 10 までの整数の 2 乗の配列を作成する例を次に示します。
let array3 = [| for i in 1 .. 10 -> i * i |]
すべての要素が 0 に初期化される配列を作成するには、Array.zeroCreate を使用します。
let arrayOfTenZeroes : int array = Array.zeroCreate 10
要素へのアクセス
配列の要素には、ドット演算子 (.) と角かっこ ([ と ]) を使用してアクセスできます。
array1.[0]
配列のインデックスは 0 から始まります。
また、スライス表記を使用して配列の要素にアクセスすることもできます。この方法では、配列のサブ範囲を指定できます。 スライス表記の例を次に示します。
// Accesses elements from 0 to 2.
array1.[0..2]
// Accesses elements from the beginning of the array to 2.
array1.[..2]
// Accesses elements from 2 to the end of the array.
array1.[2..]
スライス表記を使用するときは、配列の新しいコピーが作成されます。
配列の型とモジュール
F# の配列の型はすべて、.NET Framework 型の Array です。 したがって、F# の配列では、Array で使用できるすべての機能がサポートされます。
ライブラリ モジュール Microsoft.FSharp.Collections.Array は、1 次元配列の操作をサポートします。 Array2D、Array3D、Array4D の各モジュールには、それぞれ、2 次元、3 次元、4 次元の配列の操作をサポートする関数があります。 4 より大きいランクの配列は、Array を使用して作成できます。
単純な関数
Array.get は要素を取得します。 Array.length は配列の長さを提供します。 Array.set は、1 つの要素を指定した値に設定します。 これらの関数の使い方を次のコード例に示します。
let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
printf "%s " (Array.get array1 i)
出力は次のとおりです。
0 1 2 3 4 5 6 7 8 9
配列を作成する関数
既存の配列を必要とせずに配列を作成できる関数がいくつかあります。 Array.empty は、要素をまったく含まない新しい配列を作成します。 Array.create は、指定したサイズの配列を作成し、すべての要素を指定した値に設定します。 Array.init は、次元が指定された配列と、要素を生成する関数を作成します。 Array.zeroCreate は、すべての要素が配列の型のゼロ値に初期化された配列を作成します。 これらの関数の例を次のコードに示します。
let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length
printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)
printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))
let (myZeroArray : float array) = Array.zeroCreate 10
出力は次のとおりです。
Length of empty array: 0
Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Array.copy は、既存の配列からコピーした要素を含む新しい配列を作成します。 コピーは簡易コピーです。つまり、要素の型が参照型である場合は、参照だけがコピーされ、基になっているオブジェクトはコピーされません。 これを次のコード例に示します。
open System.Text
let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray.[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray.[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray
このコードによる出力は、次のようになります。
[|Test1; Test2; |]
[|; Test2; |]
Test1 という文字列は、最初の配列にのみ表示されます。これは、firstArray の参照が、新しい要素を作成する操作によって上書きされるものの、空の文字列への元の参照は影響を受けず、secondArray にまだ存在しているためです。 Test2 という文字列は、両方の配列で表示されます。これは、StringBuilder 型に対する Insert 操作は基になっている StringBuilder オブジェクトに影響を与え、このオブジェクトは両方の配列で参照されているためです。
Array.sub は、配列のサブ範囲から新しい配列を生成します。 サブ範囲は、開始インデックスと長さで指定します。 Array.sub を使用したコードの例を次に示します。
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
出力では、サブ配列が要素 5 から開始し、10 個の要素を含むことが示されています。
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
Array.append は、既存の 2 つの配列を結合することで、新しい配列を作成します。
次のコードで Array.append の例を示します。
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
このコードによる出力は、次のようになります。
[|1; 2; 3; 4; 5; 6|]
Array.choose は、新しい配列に含める配列の要素を選択します。 次のコードは Array.choose の例です。 配列の要素の型は、オプションの型で返される値の型と一致している必要はありません。 この例では、要素の型は int ですが、オプションは多項式関数 elem*elem - 1 の結果であり、浮動小数点数です。
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
このコードによる出力は、次のようになります。
[|3.0; 15.0; 35.0; 63.0; 99.0|]
Array.collect は、指定された関数を既存の配列の各要素に対して実行し、その関数によって生成された要素を収集して、それらの要素を新しい配列にまとめます。 次のコードは Array.collect の例です。
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
このコードによる出力は、次のようになります。
[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Array.concat は、一連の配列を受け取り、1 つの配列にまとめます。 次のコードは Array.concat の例です。
let multiplicationTable max = seq { for i in 1 .. max -> [| for j in 1 .. max -> (i, j, i*j) |] }
printfn "%A" (Array.concat (multiplicationTable 3))
このコードによる出力は、次のようになります。
[|(1, 1, 1); (1, 2, 2); (1, 3, 3); (2, 1, 2); (2, 2, 4); (2, 3, 6); (3, 1, 3);
(3, 2, 6); (3, 3, 9)|]
Array.filter は、ブール条件の関数を受け取り、入力配列の要素のうち、条件が true の要素のみを含む新しい配列を生成します。 次のコードは Array.filter の例です。
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
このコードによる出力は、次のようになります。
[|2; 4; 6; 8; 10|]
Array.rev は、既存の配列を逆順にした新しい配列を生成します。 次のコードは Array.rev の例です。
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
このコードによる出力は、次のようになります。
"Hello world!"
次の例に示すように、パイプライン演算子 (|>) を使用することで、配列モジュールの配列変換関数を簡単に結合できます。
[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"
出力は次のようになります。
[|100; 36; 16; 4|]
多次元配列
多次元配列を作成できますが、多次元配列リテラルを記述するための構文はありません。 配列要素の一連のシーケンスから配列を作成するには、array2D 演算子を使用します。 シーケンスには、配列リテラルまたはリスト リテラルを使用できます。 たとえば、次のコードでは 2 次元の配列が作成されます。
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
また、Array2D.init 関数を使用すると、2 次元の配列を初期化できます。3 次元および 4 次元の配列にも同様の関数があります。 これらの関数は、要素の作成に使用する関数を受け取ります。 関数を指定するのではなく、初期値に設定された要素を含む 2 次元配列を作成するには、Array2D.create 関数を使用します。この関数は、最高で 4 次元までの配列にも使用できます。 次のコード例では、まず、目的の要素を含む複数の配列から成る 1 つの配列を作成し、次に、Array2D.init を使用して目的の 2 次元配列を生成する方法を示します。
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays.[i].[j])
配列インデックスとスライス構文は、4 ランクまでの配列に使用できます。 複数の次元のインデックスを指定する場合は、次のコード例に示すように、コンマを使用してインデックスを区切ります。
twoDimensionalArray.[0, 1] <- 1.0
2 次元配列の型は <type>[,] として書き出され (int[,]、double[,] など)、3 次元配列の型は <type>[,,] として書き出されます。このように、次元が高くなるにつれ、書き出される型が変わります。
1 次元配列で使用できる関数のサブセットのうち、多次元配列でも使用できるのは一部だけです。 詳細については、「Collections.Array モジュール (F#)」、「Collections.Array2D モジュール (F#)」、「Collections.Array3D モジュール (F#)」、および「Collections.Array4D モジュール (F#)」を参照してください。
配列に対するブール条件
Array.exists 関数と Array.exists2 関数は、それぞれ 1 つまたは 2 つの配列の要素をテストします。 これらの関数は、テスト用の関数を受け取り、要素 (または Array.exists2 の場合は要素のペア) のうち、条件を満たす要素がある場合は true を返します。
次のコードは、Array.exists と Array.exists2 の使用方法を示しています。 これらの例では、ただ 1 つの引数 (この場合は関数引数) を適用することで、新しい関数を作成しています。
let allNegative = Array.exists (fun elem -> abs (elem) = elem) >> not
printfn "%A" (allNegative [| -1; -2; -3 |])
printfn "%A" (allNegative [| -10; -1; 5 |])
printfn "%A" (allNegative [| 0 |])
let haveEqualElement = Array.exists2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (haveEqualElement [| 1; 2; 3 |] [| 3; 2; 1|])
このコードによる出力は、次のようになります。
true
false
false
true
同様に、Array.forall 関数は、1 つの配列をテストして、すべての要素がブール条件を満たすかどうかを判別します。 類似関数の Array.forall2 は、同じ長さの 2 つの配列の要素を対象とするブール関数を使用して、同じ機能を実行します。 これらの関数の使い方を次のコード例に示します。
let allPositive = Array.forall (fun elem -> elem > 0)
printfn "%A" (allPositive [| 0; 1; 2; 3 |])
printfn "%A" (allPositive [| 1; 2; 3 |])
let allEqual = Array.forall2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (allEqual [| 1; 2 |] [| 1; 2 |])
printfn "%A" (allEqual [| 1; 2 |] [| 2; 1 |])
これらの例の出力は次のようになります。
false
true
true
false
配列の検索
Array.find は、ブール関数を受け取り、その関数から true が返る最初の要素を返します。条件を満たす要素が見つからない場合は、KeyNotFoundException が発生します。 Array.findIndex は Array.find に似ていますが、要素自体ではなく、要素のインデックスを返す点が異なります。
次のコードでは、Array.find と Array.findIndex を使用して、完全平方かつ完全立方である値を探しています。
let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index
出力は次のとおりです。
The first element that is both a square and a cube is 64 and its index is 62.
Array.tryFind は Array.find に似ていますが、結果がオプション型であり、要素が見つからない場合は None を返す点が異なります。 一致する要素が配列内にあるかどうかわからない場合は、Array.find ではなく、Array.tryFind を使用してください。 同様に、Array.tryFindIndex は Array.findIndex に似ていますが、戻り値がオプション型である点が異なります。 要素が見つからない場合、オプションは None です。
Array.tryFind を使用したコードの例を次に示します。 このコードでは、前に示したコードを利用しています。
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let lookForCubeAndSquare array1 =
let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
match result with
| Some x -> printfn "Found an element: %d" x
| None -> printfn "Failed to find a matching element."
lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]
出力は次のとおりです。
Found an element: 1
Found an element: 729
要素を見つけるだけでなく、変換する必要がある場合は、Array.tryPick を使用します。 この関数の結果は、変換した要素をオプションの値として返した最初の要素になります。該当する要素が見つからない場合は、None を返します。
Array.tryPick の使用方法を次のコードに示します。 この例では、ラムダ式の代わりに、複数のローカル ヘルパー関数を定義することでコードを簡単にしています。
let findPerfectSquareAndCube array1 =
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
// intFunction : (float -> float) -> int -> int
// Allows the use of a floating point function with integers.
let intFunction function1 number = int (round (function1 (float number)))
let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
// testElement: int -> (int * int * int) option
// Test an element to see whether it is a perfect square and a perfect
// cube, and, if so, return the element, square root, and cube root
// as an option value. Otherwise, return None.
let testElement elem =
if isPerfectSquare elem && isPerfectCube elem then
Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
else None
match Array.tryPick testElement array1 with
| Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
| None -> printfn "Did not find an element that is both a perfect square and a perfect cube."
findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]
出力は次のとおりです。
Found an element 1 with square root 1 and cube root 1.
Found an element 64 with square root 8 and cube root 4.
Found an element 729 with square root 27 and cube root 9.
Found an element 4096 with square root 64 and cube root 16.
配列に対する計算の実行
Array.average 関数は、配列の各要素の平均を返します。 この関数を使用できるのは、整数による正確な除算がサポートされている要素型だけです。そのため、浮動小数点型では使用できますが、整数型では使用できません。 Array.averageBy 関数は、各要素に対して関数を呼び出した結果の平均を返します。 整数型の配列の場合は、Array.averageBy を使用し、この関数で各要素を浮動小数点型に変換して計算することもできます。
最大の要素または最小の要素を取得するには、Array.max または Array.min を使用します (要素型でサポートされている場合)。 同様の関数である Array.maxBy と Array.minBy では、比較がサポートされている型に変換する目的などのために、別の関数を最初に実行できます。
Array.sum は、配列の各要素を加算します。Array.sumBy は、各要素に対して関数を呼び出し、その結果を加算します。
戻り値を保存しないで、配列の各要素に対して関数を実行するには、Array.iter を使用します。 同じ長さの 2 つの配列を対象とする関数の場合は、Array.iter2 を使用します。 また、関数の結果の配列を保持する必要がある場合は、Array.map または Array.map2 を使用します。後者は 2 つの配列を一度に処理します。
類似関数の Array.iteri と Array.iteri2 では、要素のインデックスを計算に含めることができます。Array.mapi と Array.mapi2 についても同様です。
Array.fold、Array.foldBack、Array.reduce、Array.reduceBack、Array.scan、Array.scanBack の各関数は、配列のすべての要素を対象とするアルゴリズムを実行します。 同様に、類似関数の Array.fold2 と Array.foldBack2 は、2 つの配列に対して計算を実行します。
計算を実行するこれらの関数は、List モジュールの同じ名前の関数に対応しています。 使用例については、「リスト (F#)」を参照してください。
配列の変更
Array.set は、1 つの要素を指定した値に設定します。 Array.fill は、配列内の特定範囲の要素を、指定した値に設定します。 Array.fill のコード例を次に示します。
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
出力は次のとおりです。
[|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]
Array.blit を使用すると、1 つの配列の一部を別の配列にコピーできます。
他の型との相互変換
Array.ofList は、リストから配列を作成します。 Array.ofSeq は、シーケンスから配列を作成します。 Array.toList と Array.toSeq は、配列型から別のコレクション型に変換します。
配列の並べ替え
汎用の比較関数を使用して配列を並べ替えるには、Array.sort を使用します。 キーと呼ばれる値を生成する関数を指定し、そのキーに基づいて汎用の比較関数で並べ替えを行う場合は、Array.sortBy を使用します。 独自の比較関数を使用する必要がある場合は、Array.sortWith を使用します。 Array.sort、Array.sortBy、および Array.sortWith は、いずれも、並べ替えた配列を新しい配列として返します。 類似関数 Array.sortInPlace、Array.sortInPlaceBy、および Array.sortInPlaceWith は、新しい配列を返すのではなく、既存の配列を変更します。
配列と組
Array.zip 関数と Array.unzip 関数は、組のペアから成る配列を、配列から成る組に変換します。また、その逆の変換も行います。 Array.zip3 と Array.unzip3 も同様ですが、3 つの要素または 3 つの配列から成る組を対象とする点が異なります。
配列に対する並列計算
Array.Parallel モジュールには、配列に対して並列計算を実行するための関数が含まれます。 このモジュールは、Version 4 より前の .NET Framework を対象とするアプリケーションでは使用できません。