Scala の sliding メソッドを作成してみる

Scala には、sliding メソッドというのがあります。これを C# で実装してみました。

using System;
using System.Collections.Generic;
using System.Linq;

static class Ext {
    public static IEnumerable<T[]> Sliding<T>(this IEnumerable<T> src, int size, int step=1) {
        var xs = new List<T>();
        int index = 0;
        int headIndex = 0;
        bool added = false;
        foreach (var e in src) {
            if (index >= headIndex) {
                xs.Add(e);
                added = true;
                if (xs.Count == size) {
                    yield return xs.ToArray();
                    added = false;
                    headIndex += step;
                    xs = xs.Skip(step).ToList();
                }
            }
            index++;
        }
        if (xs.Count > 0 && added) yield return xs.ToArray();
    }
}

class Program {
    // 出力確認用
    static void Display<T>(IEnumerable<T[]> xs) {
        var ls = xs.Select(e => "[" + string.Join(",", e) + "]");
        Console.WriteLine("[" + string.Join(",", ls) + "]");
    }

    static void Main() {
        var xs = new[] { 1, 2, 3, 4, 5 };
        for (int i = 1; i <= xs.Length; i++) {
            Display(xs.Sliding(i));
        }

        Console.WriteLine("2 個ずつにする:");
        Display(xs.Sliding(2, 2));
    }
}

実行結果です。

[[1],[2],[3],[4],[5]]
[[1,2],[2,3],[3,4],[4,5]]
[[1,2,3],[2,3,4],[3,4,5]]
[[1,2,3,4],[2,3,4,5]]
[[1,2,3,4,5]]
2 個ずつにする:
[[1,2],[3,4],[5]]

Sliding メソッドがあると、階差数列を求めたり、要素を N 個ごとにグルーピングするのが簡単にできるようになります。

階差数列を求める

var xs = new[] { 1, 6, 15, 28, 45, 66 };
var ys = xs.Sliding(2).Select(e => e[1] - e[0]);
Console.WriteLine(string.Join(", ", ys));

隣り合う要素の差を求めています。

実行結果:

5, 9, 13, 17, 21

連続する重複要素をひとつにまとめる

static int[] Compact(int[] xs) {
    if (xs.Length <= 1) return xs;
    return xs.Take(1).Concat(xs.Sliding(2).Where(e => e[0] != e[1]).Select(e => e[1])).ToArray();
}

// 連続している重複要素をひとつにする
var xs = new[] { 1, 1, 2, 3, 3, 3, 4, 2, 2, 5 };
var a = Compact(xs);
Console.WriteLine(string.Join(", ", a));

Compact メソッドでは、隣り合う要素が違いに異なるもののみを Where で取り出して、Select で 2 番目の要素を取り出しています。こうすると連続する重複要素を取り除くことが出来ます。

実行結果:

1, 2, 3, 4, 2, 5

要素を N 個ごとにグループ化する

var xs = Enumerable.Range(0, 10);
// 3 個ごとにグループ化する
Display(xs.Sliding(3, 3));

実行結果:

[[0,1,2],[3,4,5],[6,7,8],[9]]