スライス

この記事では、既存の F# の型からスライスを取得する方法と、独自のスライスを定義する方法について説明します。

F# では、スライスは任意のデータ型のサブセットです。 スライスはインデクサーに似ていますが、基になるデータ構造から 1 つの値が生成されるのではなく、複数の値が生成されます。 データ型で指定されたインデックスの範囲を選択するために、スライスには .. 演算子構文が使用されます。 詳細については、ループ式のリファレンスの記事を参照してください。

現在、F# には、文字列、一覧、配列、および多次元 (2D、3D、4D) 配列のスライスのサポートが組み込まれています。 スライスは、F# の配列と一覧で最もよく使用されています。 型定義またはスコープ内の型拡張GetSlice メソッドを使用して、カスタム データ型にスライスを追加することができます。

F# の一覧と配列のスライス

スライスされる最も一般的なデータ型は、F# の一覧と配列です。 次の例は、一覧をスライスする方法を示しています。

// Generate a list of 100 integers
let fullList = [ 1 .. 100 ]

// Create a slice from indices 1-5 (inclusive)
let smallSlice = fullList[1..5]
printfn $"Small slice: {smallSlice}"

// Create a slice from the beginning to index 5 (inclusive)
let unboundedBeginning = fullList[..5]
printfn $"Unbounded beginning slice: {unboundedBeginning}"

// Create a slice from an index to the end of the list
let unboundedEnd = fullList[94..]
printfn $"Unbounded end slice: {unboundedEnd}"

配列のスライスは、一覧のスライスと同じです。

// Generate an array of 100 integers
let fullArray = [| 1 .. 100 |]

// Create a slice from indices 1-5 (inclusive)
let smallSlice = fullArray[1..5]
printfn $"Small slice: {smallSlice}"

// Create a slice from the beginning to index 5 (inclusive)
let unboundedBeginning = fullArray[..5]
printfn $"Unbounded beginning slice: {unboundedBeginning}"

// Create a slice from an index to the end of the list
let unboundedEnd = fullArray[94..]
printfn $"Unbounded end slice: {unboundedEnd}"

スライスでは、F# 6 より前では、追加の . と共に expr.[start..finish] 構文が使用されていました。 希望する場合は、この構文はまだ使用できます。 詳細については、「RFC FS-1110」を参照してください。

多次元配列のスライス

F# は、F# コア ライブラリで多次元配列をサポートしています。 1 次元配列と同様に、多次元配列のスライスも便利です。 ただし、次元が追加されているので、特定の行と列のスライスを取得できるように、若干異なる構文が必要になります。

次の例は、2D 配列をスライスする方法を示しています。

// Generate a 3x3 2D matrix
let A = array2D [[1;2;3];[4;5;6];[7;8;9]]
printfn $"Full matrix:\n {A}"

// Take the first row
let row0 = A[0,*]
printfn $"{row0}"

// Take the first column
let col0 = A[*,0]
printfn $"{col0}"

// Take all rows but only two columns
let subA = A[*,0..1]
printfn $"{subA}"

// Take two rows and all columns
let subA' = A[0..1,*]
printfn $"{subA}"

// Slice a 2x2 matrix out of the full 3x3 matrix
let twoByTwo = A[0..1,0..1]
printfn $"{twoByTwo}"

他のデータ構造のスライスを定義する

F# コア ライブラリには、限られたセットの型に対してスライスが定義されています。 さらに多くのデータ型のスライスを定義する場合は、型定義自体または型拡張のいずれかを使用できます。

たとえば、ArraySegment<T> クラスに対してスライスを定義し、データ操作を便利にする方法を紹介します。

open System

type ArraySegment<'TItem> with
    member segment.GetSlice(start, finish) =
        let start = defaultArg start 0
        let finish = defaultArg finish segment.Count
        ArraySegment(segment.Array, segment.Offset + start, finish - start)

let arr = ArraySegment [| 1 .. 10 |]
let slice = arr[2..5] //[ 3; 4; 5]

Span<T>ReadOnlySpan<T> の型を使用したもう 1 つの例を次に示します。

open System

type ReadOnlySpan<'T> with
    member sp.GetSlice(startIdx, endIdx) =
        let s = defaultArg startIdx 0
        let e = defaultArg endIdx sp.Length
        sp.Slice(s, e - s)

type Span<'T> with
    member sp.GetSlice(startIdx, endIdx) =
        let s = defaultArg startIdx 0
        let e = defaultArg endIdx sp.Length
        sp.Slice(s, e - s)

let printSpan (sp: Span<int>) =
    let arr = sp.ToArray()
    printfn $"{arr}"

let sp = [| 1; 2; 3; 4; 5 |].AsSpan()
printSpan sp[0..] // [|1; 2; 3; 4; 5|]
printSpan sp[..5] // [|1; 2; 3; 4; 5|]
printSpan sp[0..3] // [|1; 2; 3|]
printSpan sp[1..3] // |2; 3|]

組み込みの F# スライスは両端の値を含む

F# のすべての組み込みスライスは両端の値を含みます。つまり、上限はスライスに含まれます。 開始インデックスが x、終了インデックスが y であるスライスがある場合、結果のスライスには yth 値が含まれます。

// Define a new list
let xs = [1 .. 10]

printfn $"{xs[2..5]}" // Includes the 5th index

組み込みの F# の空のスライス

構文によって存在しないスライスが生成される可能性がある場合、F# の一覧、配列、シーケンス、文字列、多次元 (2D、3D、4D) 配列のいずれも、空のスライスが生成されます。

次の例を確認してください。

let l = [ 1..10 ]
let a = [| 1..10 |]
let s = "hello!"

let emptyList = l[-2..(-1)]
let emptyArray = a[-2..(-1)]
let emptyString = s[-2..(-1)]

重要

C# 開発者であれば、空のスライスが生成されるのではなく、例外がスローされることを期待するかもしれません。 これは、F# では空のコレクションが構成されるという事実に基づいた設計上の決定です。 空の F# の一覧は、別の F# の一覧で構成される可能性があります。また、空の文字列が既存の文字列に追加されるなどの可能性があります。 パラメーターとして渡された値に基づいてスライスを受け取ることはよくあることです。また、空のコレクションを生成することで範囲外 > を許容することは、F# コードの構成の性質に適合しています。

3D および 4D 配列の固定インデックス スライス

F# の 3D および 4D 配列の場合、特定のインデックスを "修正" し、修正されたそのインデックスを使用して他の次元をスライスできます。

これについて説明するために、次の 3D 配列を考えてみます。

z = 0

x\y 0 1
0 0 1
1 2 3

z = 1

x\y 0 1
0 4 5
1 6 7

配列からスライス [| 4; 5 |] を抽出する場合は、固定インデックス スライスを使用します。

let dim = 2
let m = Array3D.zeroCreate<int> dim dim dim

let mutable count = 0

for z in 0..dim-1 do
    for y in 0..dim-1 do
        for x in 0..dim-1 do
            m[x,y,z] <- count
            count <- count + 1

// Now let's get the [4;5] slice!
m[*, 0, 1]

最後の行は、3D 配列の y および z インデックスを修正し、マトリックスに対応する残りの x 値を取得します。

関連項目