シーケンス
"シーケンス" は、すべてが 1 つの型である論理的な一連の要素です。 シーケンスは、順序付けられた大量のデータのコレクションがあり、必ずしもすべての要素を使用するとは限らない場合に特に便利です。 シーケンスの個々の要素は必要なときにのみ計算されるので、すべての要素が使用されるわけではない状況では、リストより優れたパフォーマンスを提供できます。 シーケンスは、IEnumerable<T> のエイリアスである seq<'T>
型によって表されます。 したがって、IEnumerable<T> インターフェイスを実装するすべての .NET 型をシーケンスとして使用できます。 Seq モジュールにより、シーケンスに関連する操作のサポートが提供されます。
シーケンス式
"シーケンス式" は、シーケンスとして評価される式です。 シーケンス式は、さまざまな形式を取ることができます。 最も単純な形式は範囲を指定するものです。 たとえば、seq { 1 .. 5 }
により作成されるシーケンスには、エンドポイント 1 と 5 を含む 5 つの要素が格納されます。 2 組の 2 個のピリオドの間でインクリメント (またはデクリメント) を指定することもできます。 たとえば、次のコードでは、10 の倍数のシーケンスが作成されます。
// Sequence that has an increment.
seq { 0..10..100 }
シーケンス式は、シーケンスの値を生成する F# の式で構成されます。 プログラムで値を生成することもできます。
seq { for i in 1..10 -> i * i }
前のサンプルで使用されている ->
演算子により、値がシーケンスの一部になる式を指定できます。 ->
は、その後に続くコードのすべての部分で値が返される場合にのみ使用できます。
または、do
キーワードと、それに続く省略可能な yield
を指定することもできます。
seq {
for i in 1..10 do
yield i * i
}
// The 'yield' is implicit and doesn't need to be specified in most cases.
seq {
for i in 1..10 do
i * i
}
次のコードからは、座標ペアのリストと共に、グリッドを表す配列へのインデックスが生成されます。 最初の for
式で、do
を指定する必要があることに注意してください。
let (height, width) = (10, 10)
seq {
for row in 0 .. width - 1 do
for col in 0 .. height - 1 -> (row, col, row * width + col)
}
シーケンスで使用される if
式はフィルターです。 たとえば、int -> bool
型の isprime
関数があると仮定し、素数だけのシーケンスを生成するには、次のようにシーケンスを作成します。
seq {
for n in 1..100 do
if isprime n then
n
}
前に説明したように、if
に対応する else
分岐がないため、ここでは do
を指定する必要があります。 ->
を使用しようとすると、すべての分岐で値が返されていないというエラーが発生します。
yield!
キーワード
要素のシーケンスを別のシーケンスに含めることが必要になる場合があります。 シーケンスを別のシーケンス内に含めるには、yield!
キーワードを使用する必要があります。
// Repeats '1 2 3 4 5' ten times
seq {
for _ in 1..10 do
yield! seq { 1; 2; 3; 4; 5}
}
yield!
は、内部シーケンスをフラット化し、それを含む側のシーケンスに格納することと考えることもできます。
yield!
を式で使用するときは、他のすべての単一の値で yield
キーワードを使用する必要があります。
// Combine repeated values with their values
seq {
for x in 1..10 do
yield x
yield! seq { for i in 1..x -> i}
}
前の例では、各 x
に対して 1
から x
までのすべての値に加えて、x
の値が生成されます。
例
最初の例では、反復、フィルター、生成を含むシーケンス式を使用して、配列を生成します。 このコードを実行すると、1 から 100 までの素数のシーケンスがコンソールに出力されます。
// Recursive isprime function.
let isprime n =
let rec check i =
i > n / 2 || (n % i <> 0 && check (i + 1))
check 2
let aSequence =
seq {
for n in 1..100 do
if isprime n then
n
}
for x in aSequence do
printfn "%d" x
次の例を実行すると、3 つの要素のタプルで構成される乗算テーブルが作成されます。各要素は 2 つの因数と積で構成されています。
let multiplicationTable =
seq {
for i in 1..9 do
for j in 1..9 -> (i, j, i * j)
}
次の例は、yield!
を使用して個々のシーケンスを 1 つの最終シーケンスに結合する方法を示したものです。 この場合、バイナリ ツリー内の各サブツリーのシーケンスが再帰関数で連結されて、最後のシーケンスが生成されます。
// Yield the values of a binary tree in a sequence.
type Tree<'a> =
| Tree of 'a * Tree<'a> * Tree<'a>
| Leaf of 'a
// inorder : Tree<'a> -> seq<'a>
let rec inorder tree =
seq {
match tree with
| Tree(x, left, right) ->
yield! inorder left
yield x
yield! inorder right
| Leaf x -> yield x
}
let mytree = Tree(6, Tree(2, Leaf(1), Leaf(3)), Leaf(9))
let seq1 = inorder mytree
printfn "%A" seq1
シーケンスの使用
シーケンスでは、リストと同じ関数の多くがサポートされています。 また、キー生成関数を使用したグループ化やカウントなどの操作もシーケンスでサポートされます。 さらに、サブシーケンスを抽出するためのさまざまな関数もシーケンスによりサポートされます。
リスト、配列、セット、マップなどの多くのデータ型は、列挙可能なコレクションであるため、暗黙的なシーケンスです。 引数としてシーケンスを受け取る関数は、F# の一般的なデータ型に加えて、System.Collections.Generic.IEnumerable<'T>
を実装するすべての .NET データ型でも機能し ます。 これを、引数としてリストを受け取る関数と比較してください。そのような関数は、リストだけを受け取ることができます。 型 seq<'T>
は、IEnumerable<'T>
の型略称です。 つまり、ジェネリックの System.Collections.Generic.IEnumerable<'T>
を実装するすべての型 (F# の配列、リスト、セット、マップ、およびほとんどの .NET コレクション型) は、seq
型と互換性があり、シーケンスを使用できるすべての場所で使用できます。
モジュール関数
FSharp.Collections 名前空間の Seq モジュールには、シーケンスを処理するための関数が含まれています。 これらの関数は、リスト、配列、マップ、セットでも使用できます。これらの型はすべて列挙可能であるため、シーケンスとして扱うことができます。
シーケンスの作成
シーケンスは、前に説明したようにシーケンス式を使用して、または特定の関数を使用して、作成することができます。
Seq.empty を使用すると、空のシーケンスを作成できます。または、Seq.singleton を使用すると、指定した要素 1 つだけのシーケンスを作成できます。
let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10
Seq.init を使用すると、指定した関数を使用して要素が作成されるシーケンスを作成できます。 シーケンスのサイズも指定します。 この関数は List.init と同じですが、シーケンスを反復処理するまで要素が作成されない点が異なります。 次のコードは、Seq.init
の使用例です。
let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10)
Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10
出力は次のようになります。
0 10 20 30 40
Seq.ofArray と Seq.ofList<'T> 関数を使用すると、配列とリストからシーケンスを作成できます。 ただし、キャスト演算子を使用して、配列とリストをシーケンスに変換することもできます。 どちらの手法も、次のコードで示されています。
// Convert an array to a sequence by using a cast.
let seqFromArray1 = [| 1 .. 10 |] :> seq<int>
// Convert an array to a sequence by using Seq.ofArray.
let seqFromArray2 = [| 1 .. 10 |] |> Seq.ofArray
Seq.cast を使用すると、System.Collections
で定義されるもののような、弱く型指定されたコレクションからシーケンスを作成できます。 そのような弱く型指定されたコレクションには要素型 System.Object
があり、非ジェネリックの System.Collections.Generic.IEnumerable`1
型を使用して列挙されます。 次に示すコードでは、Seq.cast
を使用して System.Collections.ArrayList
をシーケンスに変換しています。
open System
let arr = ResizeArray<int>(10)
for i in 1 .. 10 do
arr.Add(10)
let seqCast = Seq.cast arr
Seq.initInfinite 関数を使用すると、無限のシーケンスを定義できます。 このようなシーケンスの場合、要素のインデックスから各要素を生成する関数を指定します。 無限シーケンスは、レイジー評価のために可能です。要素は、指定した関数を呼び出すことにより、必要に応じて作成されます。 次のコード例を実行すると、浮動小数点数の無限のシーケンスが生成されます。この例では、連続した整数の 2 乗の逆数の交代級数です。
let seqInfinite =
Seq.initInfinite (fun index ->
let n = float (index + 1)
1.0 / (n * n * (if ((index + 1) % 2 = 0) then 1.0 else -1.0)))
printfn "%A" seqInfinite
Seq.unfold を使用すると、状態を受け取り、それを変換してシーケンス内の後続の各要素を生成するコンピュテーション関数から、シーケンスが生成されます。 状態は、各要素を計算するために使用される値であり、各要素が計算されたら変更できます。 Seq.unfold
の 2 番目の引数は、シーケンスを開始するために使用される初期値です。 Seq.unfold
では状態のオプション型が使用されるので、None
値を返すことによってシーケンスを終了できます。 次のコードでは、unfold
操作によって生成される 2 つのシーケンス seq1
と fib
の例が示されています。 1 つ目の seq1
は、20 までの数値の単純なシーケンスです。 2 つ目の fib
では、unfold
を使用してフィボナッチ数列が計算されています。 フィボナッチ数列内の各要素は前の 2 つのフィボナッチ数の合計であるため、状態の値は、シーケンス内の前の 2 つの数値で構成されるタプルになります。 初期値は (0,1)
で、シーケンスの最初の 2 つの数値です。
let seq1 =
0 // Initial state
|> Seq.unfold (fun state ->
if (state > 20) then
None
else
Some(state, state + 1))
printfn "The sequence seq1 contains numbers from 0 to 20."
for x in seq1 do
printf "%d " x
let fib =
(0, 1)
|> Seq.unfold (fun state ->
let cur, next = state
if cur < 0 then // overflow
None
else
let next' = cur + next
let state' = next, next'
Some (cur, state') )
printfn "\nThe sequence fib contains Fibonacci numbers."
for x in fib do printf "%d " x
出力は次のようになります。
The sequence seq1 contains numbers from 0 to 20.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The sequence fib contains Fibonacci numbers.
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181
次に示すのは、ここで説明したシーケンス モジュール関数の多くを使用して無限シーケンスの値を生成および計算するコードの例です。 このコードの実行には数分かかることがあります。
// generateInfiniteSequence generates sequences of floating point
// numbers. The sequences generated are computed from the fDenominator
// function, which has the type (int -> float) and computes the
// denominator of each term in the sequence from the index of that
// term. The isAlternating parameter is true if the sequence has
// alternating signs.
let generateInfiniteSequence fDenominator isAlternating =
if (isAlternating) then
Seq.initInfinite (fun index ->
1.0 /(fDenominator index) * (if (index % 2 = 0) then -1.0 else 1.0))
else
Seq.initInfinite (fun index -> 1.0 /(fDenominator index))
// The harmonic alternating series is like the harmonic series
// except that it has alternating signs.
let harmonicAlternatingSeries = generateInfiniteSequence (fun index -> float index) true
// This is the series of reciprocals of the odd numbers.
let oddNumberSeries = generateInfiniteSequence (fun index -> float (2 * index - 1)) true
// This is the series of recipocals of the squares.
let squaresSeries = generateInfiniteSequence (fun index -> float (index * index)) false
// This function sums a sequence, up to the specified number of terms.
let sumSeq length sequence =
(0, 0.0)
|>
Seq.unfold (fun state ->
let subtotal = snd state + Seq.item (fst state + 1) sequence
if (fst state >= length) then
None
else
Some(subtotal, (fst state + 1, subtotal)))
// This function sums an infinite sequence up to a given value
// for the difference (epsilon) between subsequent terms,
// up to a maximum number of terms, whichever is reached first.
let infiniteSum infiniteSeq epsilon maxIteration =
infiniteSeq
|> sumSeq maxIteration
|> Seq.pairwise
|> Seq.takeWhile (fun elem -> abs (snd elem - fst elem) > epsilon)
|> List.ofSeq
|> List.rev
|> List.head
|> snd
// Compute the sums for three sequences that converge, and compare
// the sums to the expected theoretical values.
let result1 = infiniteSum harmonicAlternatingSeries 0.00001 100000
printfn "Result: %f ln2: %f" result1 (log 2.0)
let pi = Math.PI
let result2 = infiniteSum oddNumberSeries 0.00001 10000
printfn "Result: %f pi/4: %f" result2 (pi/4.0)
// Because this is not an alternating series, a much smaller epsilon
// value and more terms are needed to obtain an accurate result.
let result3 = infiniteSum squaresSeries 0.0000001 1000000
printfn "Result: %f pi*pi/6: %f" result3 (pi*pi/6.0)
要素の検索と検出
シーケンスでは、リストで使用できる次の機能がサポートされています: Seq.exists、Seq.exists2、Seq.find、Seq.findIndex、Seq.pick、Seq.tryFind、Seq.tryFindIndex。 シーケンスに使用できるこれらの関数のバージョンの場合、シーケンスが評価されるのは検索対象の要素までだけです。 例については、「リスト」を参照してください。
サブシーケンスの取得
Seq.filter と Seq.choose は、リストで使用できる対応する関数と似ていますが、シーケンスの要素が評価されるまでフィルター処理と選択が行われない点が異なります。
Seq.truncate を使用すると、別のシーケンスからシーケンスが作成されますが、シーケンスの要素は指定した数に制限されます。 Seq.take を使用すると、シーケンスの先頭から指定し数の要素のみを含む新しいシーケンスが成されます。 シーケンス内の要素が、取得するよう指定した数より少ない場合は、Seq.take
で System.InvalidOperationException
がスローされます。 Seq.take
と Seq.truncate
の違いは、Seq.truncate
の場合、要素の数が指定した数より少なくてもエラーが生成されない点です。
次のコードは、Seq.truncate
と Seq.take
の動作と違いを示しています。
let mySeq = seq { for i in 1 .. 10 -> i*i }
let truncatedSeq = Seq.truncate 5 mySeq
let takenSeq = Seq.take 5 mySeq
let truncatedSeq2 = Seq.truncate 20 mySeq
let takenSeq2 = Seq.take 20 mySeq
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
// Up to this point, the sequences are not evaluated.
// The following code causes the sequences to be evaluated.
truncatedSeq |> printSeq
truncatedSeq2 |> printSeq
takenSeq |> printSeq
// The following line produces a run-time error (in printSeq):
takenSeq2 |> printSeq
エラーが発生する前の出力は次のとおりです。
1 4 9 16 25
1 4 9 16 25 36 49 64 81 100
1 4 9 16 25
1 4 9 16 25 36 49 64 81 100
Seq.takeWhile を使用すると、述語関数 (ブール関数) を指定して、別のシーケンスから、述語が true
になる元のシーケンスの要素で構成されるシーケンスを作成できます。ただし、述語が false
返す最初の要素の前に停止します。 Seq.skip からは、別のシーケンスの先頭から指定した数の要素をスキップした、残りの要素で構成されるシーケンスが返されます。 Seq.skipWhile からは、別のシーケンスの先頭から、述語によって true
が返される要素がスキップされた、残りの要素で構成されるシーケンスが返されます。このシーケンスは、述語が false
返す最初の要素から始まります。
次のコード例では、Seq.takeWhile
、Seq.skip
、Seq.skipWhile
の動作とその違いを示します。
// takeWhile
let mySeqLessThan10 = Seq.takeWhile (fun elem -> elem < 10) mySeq
mySeqLessThan10 |> printSeq
// skip
let mySeqSkipFirst5 = Seq.skip 5 mySeq
mySeqSkipFirst5 |> printSeq
// skipWhile
let mySeqSkipWhileLessThan10 = Seq.skipWhile (fun elem -> elem < 10) mySeq
mySeqSkipWhileLessThan10 |> printSeq
出力は次のとおりです。
1 4 9
36 49 64 81 100
16 25 36 49 64 81 100
シーケンスの変換
Seq.pairwise を使用すると、入力シーケンスの連続する要素がタプルにグループ化された新しいシーケンスが作成されます。
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
let seqPairwise = Seq.pairwise (seq { for i in 1 .. 10 -> i*i })
printSeq seqPairwise
printfn ""
let seqDelta = Seq.map (fun elem -> snd elem - fst elem) seqPairwise
printSeq seqDelta
Seq.windowed は Seq.pairwise
に似ていますが、タプルのシーケンスが生成されるのではなく、シーケンスから隣接する要素 ("ウィンドウ") のコピーが格納された配列のシーケンスが生成されます。 各配列に格納する隣接する要素の数を指定します。
次のコード例は、Seq.windowed
の使用方法を示します。 この場合、ウィンドウ内の要素の数は 3 です。 この例で使用されている printSeq
は、前のコード例で定義されています。
let seqNumbers = [ 1.0; 1.5; 2.0; 1.5; 1.0; 1.5 ] :> seq<float>
let seqWindows = Seq.windowed 3 seqNumbers
let seqMovingAverage = Seq.map Array.average seqWindows
printfn "Initial sequence: "
printSeq seqNumbers
printfn "\nWindows of length 3: "
printSeq seqWindows
printfn "\nMoving average: "
printSeq seqMovingAverage
出力は次のとおりです。
初期シーケンス:
1.0 1.5 2.0 1.5 1.0 1.5
Windows of length 3:
[|1.0; 1.5; 2.0|] [|1.5; 2.0; 1.5|] [|2.0; 1.5; 1.0|] [|1.5; 1.0; 1.5|]
Moving average:
1.5 1.666666667 1.5 1.333333333
複数のシーケンスを使用する操作
Seq.zip と Seq.zip3 は、2 つまたは 3 つのシーケンスを受け取り、タプルのシーケンスを生成します。 これらの関数は、リストで使用できる対応する関数に似ています。 1 つのシーケンスの 2 つ以上のシーケンスへの分割に対応する機能はありません。 シーケンスでこの機能が必要な場合は、シーケンスをリストに変換して、List.unzip を使用します。
並べ替え、比較、グループ化
リストでサポートされている並べ替え関数は、シーケンスでも使用できます。 これには、Seq.sort と Seq.sortBy が含まれます。 これらの関数では、シーケンス全体が反復処理されます。
2 つのシーケンスを比較するには、Seq.compareWith 関数を使用します。 この関数は、連続する要素を順番に比較し、最初の等しくないペアが検出された時点で停止します。 その後にある要素は、比較に寄与しません。
Seq.compareWith
の使用方法を次のコードに示します。
let sequence1 = seq { 1 .. 10 }
let sequence2 = seq { 10 .. -1 .. 1 }
// Compare two sequences element by element.
let compareSequences =
Seq.compareWith (fun elem1 elem2 ->
if elem1 > elem2 then 1
elif elem1 < elem2 then -1
else 0)
let compareResult1 = compareSequences sequence1 sequence2
match compareResult1 with
| 1 -> printfn "Sequence1 is greater than sequence2."
| -1 -> printfn "Sequence1 is less than sequence2."
| 0 -> printfn "Sequence1 is equal to sequence2."
| _ -> failwith("Invalid comparison result.")
前のコードでは、最初の要素のみが計算されて検査され、結果は -1 になります。
Seq.countBy は、各要素の "キー" と呼ばれる値を生成する関数を受け取ります。 要素ごとにこの関数を呼び出すことにより、各要素のキーが生成されます。 その後、Seq.countBy
からは、キー値が格納されたシーケンスと、キーの各値を生成した要素の数が返されます。
let mySeq1 = seq { 1.. 100 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1
let seqResult =
mySeq1
|> Seq.countBy (fun elem ->
if elem % 3 = 0 then 0
elif elem % 3 = 1 then 1
else 2)
printSeq seqResult
出力は次のとおりです。
(1, 34) (2, 33) (0, 33)
前の出力では、キー 1 は元のシーケンスの 34 個の要素によって生成され、キー 2 は 33 個、キー 0 は 33 個の値で生成されたことが示されています。
シーケンスの要素をグループ化するには、Seq.groupBy を呼び出します。 Seq.groupBy
は、シーケンスと、要素からキーを生成する関数を受け取ります。 この関数は、シーケンスの各要素に対して実行されます。 Seq.groupBy
からは、タプルのシーケンスが返されます。各タプルの最初の要素はキーで、2 番目はそのキーを生成した要素のシーケンスです。
次のコード例では、Seq.groupBy
を使用して、1 から 100 までの数値のシーケンスを、個別のキー値 0、1、2 を持つ 3 つのグループに分割しています。
let sequence = seq { 1 .. 100 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1
let sequences3 =
sequences
|> Seq.groupBy (fun index ->
if (index % 3 = 0) then 0
elif (index % 3 = 1) then 1
else 2)
sequences3 |> printSeq
出力は次のとおりです。
(1, seq [1; 4; 7; 10; ...]) (2, seq [2; 5; 8; 11; ...]) (0, seq [3; 6; 9; 12; ...])
Seq.distinct を呼び出すことで、重複する要素が除去されたシーケンスを作成できます。 または、Seq.distinctBy を使用することもできます。この関数は、各要素に対して呼び出されるキー生成関数を受け取ります。 結果のシーケンスには、一意のキーを持つ元のシーケンスの要素が含まれます。前にある要素と重複するキーを生成する後にある要素は破棄されます。
Seq.distinct
の使用方法を次のコード例に示します。 バイナリ数値を表すシーケンスを生成し、個別の要素が 0 と 1 だけであることを示すことによって、Seq.distinct
の使用方法が示されています。
let binary n =
let rec generateBinary n =
if (n / 2 = 0) then [n]
else (n % 2) :: generateBinary (n / 2)
generateBinary n
|> List.rev
|> Seq.ofList
printfn "%A" (binary 1024)
let resultSequence = Seq.distinct (binary 1024)
printfn "%A" resultSequence
Seq.distinctBy
の使用方法を示す次のコードでは、最初のシーケンスには負と正の数値が格納されており、キー生成関数として絶対値関数が使用されています。 結果のシーケンスには、シーケンス内の負の数値に対応する正の数値がすべてありません。これは、負の数値はシーケンス内で前の方にあり、したがって同じ絶対値つまりキーを持つ正の数値の代わりに選択されるためです。
let inputSequence = { -5 .. 10 }
let printSeq seq1 = Seq.iter (printf "%A ") seq1
printfn "Original sequence: "
printSeq inputSequence
printfn "\nSequence with distinct absolute values: "
let seqDistinctAbsoluteValue = Seq.distinctBy (fun elem -> abs elem) inputSequence
printSeq seqDistinctAbsoluteValue
読み取り専用シーケンスとキャッシュされたシーケンス
Seq.readonly は、シーケンスの読み取り専用コピーを作成します。 Seq.readonly
は、配列などの読み取り/書き込みコレクションがあり、元のコレクションを変更したくない場合に便利です。 この関数を使用して、データのカプセル化を維持できます。 次のコード例では、配列を含む型が作成されます。 プロパティにより配列が公開されますが、返されるのは配列ではなく、Seq.readonly
を使用して配列から作成されたシーケンスです。
type ArrayContainer(start, finish) =
let internalArray = [| start .. finish |]
member this.RangeSeq = Seq.readonly internalArray
member this.RangeArray = internalArray
let newArray = new ArrayContainer(1, 10)
let rangeSeq = newArray.RangeSeq
let rangeArray = newArray.RangeArray
// These lines produce an error:
//let myArray = rangeSeq :> int array
//myArray[0] <- 0
// The following line does not produce an error.
// It does not preserve encapsulation.
rangeArray[0] <- 0
Seq.cache を使用すると、シーケンスの格納されるバージョンが作成されます。 シーケンスの再評価を避ける場合、またはシーケンスを使用する複数のスレッドがあり、しかし各要素が 1 回だ処理されるようにする必要がある場合は、Seq.cache
を使用します。 複数のスレッドで使用されているシーケンスがある場合は、1 つのスレッドで元のシーケンスの値の列挙と計算を行い、残りのスレッドでキャッシュされたシーケンスを使用することができます。
シーケンスに対する計算の実行
単純な算術演算は、Seq.average、Seq.sum、Seq.averageBy、Seq.sumByなど、リストのものに似ています。
Seq.fold、Seq.reduce、Seq.scan は、リストで使用できる対応する関数に似ています。 シーケンスでは、リストでサポートされているこれらの関数の完全なバリエーションのサブセットがサポートされています。 使用例を含む詳細については、「リスト」を参照してください。
関連項目
.NET