10月 132017
 

スライス(slice)とは、列(sequence)から部分列(subsequence)を取り出す操作のことを呼ぶもの、とここでは定義しておきます。

スライスは色んな言語に実装されている機能で、特に文字列(string)から部分文字列(substring)を得る機能を実装している言語は多いでしょう。文字列に限定しないときスライスという言葉が用いられている気がします。どこ由来なのか知らないため定義もわかりません。

C#にもそれっぽい機能はありますが、ちょっと使いにくいので、Pythonのスライスに近い機能を実装してみます。

C#のスライス

C#ではスライスという言語機能は存在しません。文字列などに個別に部分列を得るメソッドを実装するという形で提供されます。

部分文字列ならSubstring()メソッドで取得できます。引数としては、部分文字列の開始インデックス、部分文字列の長さを与えます。長さを省略した場合は、指定インデックスから末尾まで取得します。

var result = "0123456789".Substring(5, 3);  // "567"

同様に、ListならGetRange()メソッドで取得できます。部分列の取得なのにGetRangeという独特な名前のせいで存在を見落としがち。string.Substring()と違って、第2引数の省略版がありません。

var list = new List<int>{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = list.GetRange(5, 3);   // 5, 6, 7

さて、配列なんですが、部分列そのものを直接返してくれるメソッドがありません。必要なサイズの配列をnewして、そこにCopyTo()する形になります。こういった配列の微妙な使いにくさは何なんですかね。

var array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = new int[3];
Array.CopyTo(array, 5, result, 0, result.Length);   // 5, 6, 7

Pythonのスライス

今回C#に実装したいスライス機能を紹介します。

Pythonではスライスを言語機能としてサポートしていて、シーケンス型(文字列やリストなど)に対して、角括弧の中にコロン区切りでインデックスを指定すると部分列を取り出せます。パラメータは開始位置と終了位置です。終了位置は切り出す最後の文字の次を指します。

result = '0123456789'[3:6]  # '345'

Pythonの面白いところは、スライスのパラメータに負数を指定できるところです。負数を指定すると、末尾から辿った位置を指定したことになります。例えば、長さ5の列に対して、インデックス-1を指定すると、インデックス4を指定したことと同義です。

result = '0123456789'[-3:9] # '678'
result = '0123456789'[3:-3] # '3456'
result = '0123456789'[-3:-2]    # '78'

このマイナス指定が割と便利なので欲しいなと思ったわけです。

ちなみに、Pythonの場合、スライスの3番目のパラメータとしてスキップ(インデックスの増分)を与えられますが、今回そこまでやらないです。

実装

拡張メソッドとして実装します。ついでに負数によるインデックス指定に対応したget/setも用意します。

文字列

public static class StringExtension
{
    public static char get(this string str, int index)
    {
        if(index < 0) {
            index += str.Length;
        }
        return str[index];
    }
    public static string slice(this string str, int begin)
    {
        if(begin < 0) {
            begin += str.Length;
        }
        return str.Substring(begin);
    }
    public static string slice(this string str, int begin, int end)
    {
        if(begin < 0) {
            begin += str.Length;
        }
        if(end < 0) {
            end += str.Length;
        }
        var length = Math.Max(0, end - begin);

        return str.Substring(begin, length);
    }
}

配列

public static class ArrayExtension
{
    public static T get<T>(this T[] array, int index)
    {
        if(index < 0) {
            index += array.Length;
        }
        return array[index];
    }
    public static void set<T>(this T[] array, int index, T value)
    {
        if(index < 0) {
            index += array.Length;
        }
        array[index] = value;
    }
    public static T[] slice<T>(this T[] array, int begin)
    {
        if(begin < 0) {
            begin += array.Length;
        }
        return slice(array, begin, array.Length);
    }
    public static T[] slice<T>(this T[] array, int begin, int end)
    {
        if(begin < 0) {
            begin += array.Length;
        }
        if(end < 0) {
            end += array.Length;
        }
        var length = Math.Max(0, end - begin);

        var newArray = new T[length];
        Array.Copy(array, begin, newArray, 0, length);
        return newArray;
    }
}

List

public static class ListExtension
{
    public static T get<T>(this IList<T> list, int index)
    {
        if(index < 0) {
            index += list.Count;
        }
        return list[index];
    }
    public static void set<T>(this IList<T> list, int index, T value)
    {
        if(index < 0) {
            index += list.Count;
        }
        list[index] = value;
    }
    public static List<T> slice<T>(this List<T> list, int begin)
    {
        if(begin < 0) {
            begin += list.Count;
        }
        return slice(list, begin, list.Count);
    }
    public static List<T> slice<T>(this List<T> list, int begin, int end)
    {
        if(begin < 0) {
            begin += list.Count;
        }
        if(end < 0) {
            end += list.Count;
        }
        var length = Math.Max(0, end - begin);

        return list.GetRange(begin, length);
    }
}

コメントを残す

Top