次の方法で共有


配列 (F#)

配列は、すべて同じ型の連続するデータ要素の固定サイズの 0 から始まる変更可能なコレクションです。

配列を作成する

配列は、いくつかの方法で作成できます。 次の例に示すように、 [||] の間の連続する値をセミコロンで区切って一覧表示することで、小さな配列を作成できます。

let array1 = [| 1; 2; 3 |]

また、各要素を個別の行に配置することもできます。この場合、セミコロン区切り記号は省略可能です。

let array1 =
    [|
        1
        2
        3
     |]

配列要素の型は、使用されるリテラルから推論され、一貫性がある必要があります。

// This is an array of 3 integers.
let array1 = [| 1; 2; 3 |]
// This is an array of a tuple of 3 integers.
let array2 = [| 1, 2, 3 |]

次のコードでは、3.0 が float で、1 と 2 が整数であるため、エラーが発生します。

// Causes an error. The 3.0 (float) cannot be converted to integer implicitly.
// let array3 = [| 1; 2; 3.0 |]

次のコードは、 1,2 がタプルであり、 3 が整数であるため、エラーも発生します。

// Causes an error too. The 3 (integer) cannot be converted to tuple implicitly.
// let array4 = [| 1, 2; 3 |]

シーケンス式を使用して配列を作成することもできます。 1 ~ 10 の整数の 2 乗配列を作成する例を次に示します。

let array3 = [| for i in 1 .. 10 -> i * i |]

すべての要素がゼロに初期化される配列を作成するには、 Array.zeroCreateを使用します。

let arrayOfTenZeroes : int array = Array.zeroCreate 10

Access 要素

角かっこ ([]) を使用して配列要素にアクセスできます。 元のドット構文 (.[index]) は引き続きサポートされていますが、F# 6.0 以降では推奨されなくなりました。

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# 配列の型は、 System.Array .NET Framework 型です。 したがって、F# 配列は、 System.Arrayで使用できるすべての機能をサポートします。

Array モジュールでは、1 次元配列に対する操作がサポートされています。 モジュール Array2DArray3D、および Array4D には、それぞれ 2 次元、3 次元、および 4 次元の配列に対する演算をサポートする関数が含まれています。 System.Arrayを使用して、4 より大きいランクの配列を作成できます。

単純な関数

Array.get は要素を取得します。 Array.length は配列の長さを示します。 Array.set は、要素を指定した値に設定します。 次のコード例は、これらの関数の使用方法を示しています。

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にまだ存在する空の文字列への元の参照には影響しないため、最初の配列にのみ表示されます。 System.Text.StringBuilder型に対するInsert操作は、両方の配列で参照される基になるSystem.Text.StringBuilder オブジェクトに影響するため、文字列Test2は両方の配列に表示されます。

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を示しています。

Array.concat [ [|0..3|] ; [|4|] ]
//output [|0; 1; 2; 3; 4|]

Array.concat [| [|0..3|] ; [|4|] |]
//output [|0; 1; 2; 3; 4|]

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 次元の配列でも使用できます。 次のコード例は、最初に目的の要素を含む配列の配列を作成し、次に 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 次元配列で使用できる関数のサブセットのみが、多次元配列でも使用できます。

配列のスライスと多次元配列

2 次元配列 (行列) では、範囲を指定し、ワイルドカード (*) 文字を使用して行または列全体を指定することで、サブマトリックスを抽出できます。

// Get rows 1 to N from an NxM matrix (returns a matrix):
matrix[1.., *]

// Get rows 1 to 3 from a matrix (returns a matrix):
matrix[1..3, *]

// Get columns 1 to 3 from a matrix (returns a matrix):
matrix[*, 1..3]

// Get a 3x3 submatrix:
matrix[1..3, 1..3]

多次元配列は、同じ次元または下位次元のサブ配列に分解できます。 たとえば、1 つの行または列を指定することで、行列からベクトルを取得できます。

// Get row 3 from a matrix as a vector:
matrix[3, *]

// Get column 3 from a matrix as a vector:
matrix[*, 3]

このスライス構文は、要素アクセス演算子とオーバーロードされた GetSlice メソッドを実装する型に使用できます。 たとえば、次のコードは、F# 2D 配列をラップする Matrix 型を作成し、配列のインデックス作成をサポートする Item プロパティを実装し、 GetSliceの 3 つのバージョンを実装します。 このコードをマトリックス型のテンプレートとして使用できる場合は、このセクションで説明するすべてのスライス操作を使用できます。

type Matrix<'T>(N: int, M: int) =
    let internalArray = Array2D.zeroCreate<'T> N M

    member this.Item
        with get(a: int, b: int) = internalArray[a, b]
        and set(a: int, b: int) (value:'T) = internalArray[a, b] <- value

    member this.GetSlice(rowStart: int option, rowFinish : int option, colStart: int option, colFinish : int option) =
        let rowStart =
            match rowStart with
            | Some(v) -> v
            | None -> 0
        let rowFinish =
            match rowFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(0) - 1
        let colStart =
            match colStart with
            | Some(v) -> v
            | None -> 0
        let colFinish =
            match colFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(1) - 1
        internalArray[rowStart..rowFinish, colStart..colFinish]

    member this.GetSlice(row: int, colStart: int option, colFinish: int option) =
        let colStart =
            match colStart with
            | Some(v) -> v
            | None -> 0
        let colFinish =
            match colFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(1) - 1
        internalArray[row, colStart..colFinish]

    member this.GetSlice(rowStart: int option, rowFinish: int option, col: int) =
        let rowStart =
            match rowStart with
            | Some(v) -> v
            | None -> 0
        let rowFinish =
            match rowFinish with
            | Some(v) -> v
            | None -> internalArray.GetLength(0) - 1
        internalArray[rowStart..rowFinish, col]

module test =
    let generateTestMatrix x y =
        let matrix = new Matrix<float>(3, 3)
        for i in 0..2 do
            for j in 0..2 do
                matrix[i, j] <- float(i) * x - float(j) * y
        matrix

    let test1 = generateTestMatrix 2.3 1.1
    let submatrix = test1[0..1, 0..1]
    printfn $"{submatrix}"

    let firstRow = test1[0,*]
    let secondRow = test1[1,*]
    let firstCol = test1[*,0]
    printfn $"{firstCol}"

配列のブール関数

関数は、それぞれ 1 つまたは 2 つの配列のテスト要素を Array.exists し、 Array.exists2 します。 これらの関数はテスト関数を受け取り、条件を満たす要素 (またはArray.exists2の要素ペア) がある場合にtrueを返します。

次のコードは、 Array.existsArray.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 配列をテストして、すべての要素がブール条件を満たすかどうかを判断します。 バリエーション 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を返す最初の要素を返すか、条件を満たす要素が見つからない場合は System.Collections.Generic.KeyNotFoundException を発生させます。 Array.findIndexArray.findに似ていますが、要素自体の代わりに要素のインデックスを返す点が異なります。

次のコードでは、 Array.findArray.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.tryFindArray.findに似ていますが、結果がオプション型であり、要素が見つからない場合は None を返します。 Array.tryFind 一致する要素が配列内にあるかどうかがわからない場合は、 Array.find の代わりに使用する必要があります。 同様に、 Array.tryFindIndexArray.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
Failed to find a matching element.

要素を検索するだけでなく、変換する必要がある場合は、 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.
Did not find an element that is both a perfect square and a perfect cube.

配列に対して計算を実行する

Array.average関数は、配列内の各要素の平均を返します。 整数による正確な除算をサポートする要素型に限定されます。これには浮動小数点型が含まれますが、整数型は含まれません。 Array.averageBy関数は、各要素で関数を呼び出した結果の平均を返します。 整数型の配列の場合は、 Array.averageBy を使用し、関数で計算のために各要素を浮動小数点型に変換できます。

要素型でサポートされている場合は、 Array.max または Array.min を使用して、最大要素または最小要素を取得します。 同様に、 Array.maxByArray.minBy を使用すると、関数を最初に実行できます。おそらく、比較をサポートする型に変換できます。

Array.sum は配列の要素を追加し、 Array.sumBy は各要素に対して関数を呼び出し、結果をまとめて追加します。

戻り値を格納せずに配列内の各要素に対して関数を実行するには、 Array.iterを使用します。 等しい長さの 2 つの配列を含む関数の場合は、 Array.iter2を使用します。 また、関数の結果の配列を保持する必要がある場合は、 Array.map または Array.map2を使用します。これは一度に 2 つの配列で動作します。

バリエーション Array.iteriArray.iteri2 により、要素のインデックスを計算に含めることができるようになります。 Array.mapiArray.mapi2についても同様です。

関数 Array.foldArray.foldBackArray.reduceArray.reduceBackArray.scanArray.scanBack は、配列のすべての要素を含むアルゴリズムを実行します。 同様に、バリエーション Array.fold2Array.foldBack2 は、2 つの配列に対して計算を実行します。

計算を実行するためのこれらの関数は、 List モジュール内の同じ名前の関数に対応します。 使用例については、「 リスト」を参照してください。

配列を変更する

Array.set は、要素を指定した値に設定します。 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を使用して、ある配列のサブセクションを別の配列にコピーできます。

他の型との間で変換する

Array.ofList は、リストから配列を作成します。 Array.ofSeq はシーケンスから配列を作成します。 Array.toListArray.toSeq 配列型からこれらの他のコレクション型に変換します。

配列の並べ替え

ジェネリック比較関数を使用して配列を並べ替えるには、 Array.sort を使用します。 Array.sortByを使用して、キーと呼ばれる値を生成する関数を指定し、キーのジェネリック比較関数を使用して並べ替えます。 カスタム比較関数を指定する場合は、 Array.sortWith を使用します。 Array.sortArray.sortBy、および Array.sortWith はすべて、並べ替えられた配列を新しい配列として返します。 バリエーション Array.sortInPlaceArray.sortInPlaceBy、および新しい配列を返す代わりに既存の配列を変更 Array.sortInPlaceWith

配列とタプル

関数は Array.zip し、 Array.unzip タプル ペアの配列を配列のタプルに変換します。その逆も同様です。 Array.zip3Array.unzip3 は似ていますが、3 つの要素のタプルまたは 3 つの配列のタプルで動作する点が異なります。

配列の並列計算

モジュール Array.Parallel には、配列に対して並列計算を実行するための関数が含まれています。 このモジュールは、バージョン 4 より前のバージョンの .NET Framework を対象とするアプリケーションでは使用できません。

こちらも参照ください