📝
カスタムスライスにはinline指定を
スライスとは
arr[start .. finish]
の書式で部分配列を取得できます。start
とfinish
は省略可能です。
これを拡張し、GetSlice()
というメソッドが用意されていれば、任意のオブジェクトに対してobj[start .. finish]
と記述できます。これをカスタムスライスと呼びます。どのような動作をさせるかはGetSlice()
メソッド作成者の自由ですが、利用者からすれば配列と同等の動作が期待されるでしょう。
カスタムスライス例
GetSlice()
のシグネチャはGetSlice(int option, int option) : 'T
となります。例えば、Span<'T>
に対してはこのような記述になるでしょうか。
type Span<'T> with
member this.GetSlice(start, finish) =
let start = match start with None -> 0 | Some start -> max 0 start
match finish with
| None -> this.Slice(start)
| Some finish -> this.Slice(start, min this.Length (finish + 1) - start)
性能懸念
スライスおよびカスタムスライスは配列のインデックスアクセスと同様に気軽に使われることが想定されるため、性能を考慮する必要があります。
問題は引数がint option
である点です。option
は参照型なため必ずGCアロケーションが発生するため、このままではカスタムスライスを気軽に使えなくなってしまいます。
そんな時はインライン展開をしましょう。 具体的にはinline
を指定します。
type Span<'T> with
member inline this.GetSlice(start, finish) =
let start = match start with None -> 0 | Some start -> max 0 start
match finish with
| None -> this.Slice(start)
| Some finish -> this.Slice(start, min this.Length (finish + 1) - start)
例えば span[..3]
と記述した場合、span.GetSlice(None, Some 3)
になります。インライン展開の上で定数式展開が行われ、span.Slice(0, min span.Length 4)
にコンパイルされるため、int option
が生成されることはなくなり、GCアロケーションを回避できます。
結論
カスタムスライスのGetSlice()
メソッドはinline
指定をしましょう。
Discussion