すもぎのめも

いろいろあったことをメモしています

シーケンスから要素を取得し合計値が指定した条件になるまで一覧を作成する拡張メソッド

いったい何を言いたいのかわからないタイトルになってしまいました。日本語難しい。 期待する結果のイメージは以下の通り。

[TestMethod]
public void 合計値が条件に一致したところで分割される()
{
    var source = "The quick brown fox jumps over the lazy dog".Split(' ');

    var expected = new[] { ToReadOnlyList(new[] { "The", "quick", "brown" }),
                           ToReadOnlyList(new[] { "fox", "jumps", "over", "the" }),
                           ToReadOnlyList(new[] { "lazy", "dog" }) };

    var actual = source.ChunkBySumIf(s => s.Length, sum => sum <= 15);

    StructuralAssert.AreEqual(expected, actual);
}

これを実現するコードがこちら。

public static IEnumerable<IReadOnlyList<T>> ChunkBySumIf<T>(this IEnumerable<T> source, Func<T, double> selector, Func<double, bool> predicate)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (selector == null) throw new ArgumentNullException(nameof(selector));
    if (predicate == null) throw new ArgumentNullException(nameof(predicate));

    using (var enumerator = source.GetEnumerator())
    {
        var values = new List<T>();
        var acc = 0.0;

        while (enumerator.MoveNext())
        {
            var x = selector(enumerator.Current);

            if (predicate(acc + x))
            {
                values.Add(enumerator.Current);
                acc += x;
            }
            else
            {
                yield return values.AsReadOnly();
                values = new List<T> { enumerator.Current };
                acc = x;
            }
        }

        if (values.Count > 0)
        {
            yield return values.AsReadOnly();
        }
    }
}

用途はかなり限定的ですが個人的にはとても役立ちました。名前は F# の chunkBySize関数と ExcelSUMIF 関数が由来です。

ここからはコードが洗練されるまでの道程です。

続きを読む

If 演算子の型判定

戻り値が Double? のとき

If IsHoge Then
    Return 5.0
End If
Return Nothing

Return If(IsHoge, 5.0, Nothing)

では、IsHoge = False の時

  • 前者 Nullable(Of T).HasValue = False
  • 後者 Nullable(Of T).HasValue = True (.Value = 0.0)

という挙動の違いが見られた。 If 演算子で Nothing が Double のデフォルトとして評価され、実質

Return If(IsHoge, 5.0, 0.0)

になってしまっている。戻り値の型に対して If 演算子の戻り値がうまいこと評価されるわけではない。

この場合で If 演算子を使うときは

Return If(IsHoge, 5.0, New Double?())

とする。

TypeScript では実装したインターフェースのメソッドより少ないパラメーターでメソッドを定義できる

TypeScript 実践プログラミングより

TypeScript実践プログラミング (Programmer's SELECTION)

TypeScript実践プログラミング (Programmer's SELECTION)

クラスのメソッドでは、インターフェースで指定されているものよりも少ない数のパラメータを定義できる。それにより、そのメソッドを実行する上で必要のない引数をクラスが無視できるようになる。

とのことなので試してみた。

interface Phone {
    call(target: string);
}

class RakurakuPhone implements Phone {
    constructor(private target: string) { }
    call() {
        console.log('Calling...');
    }
}

使うときはこんな感じで、変数の型に合わせたパラメーターでのみメソッドを呼び出せる。

var rakuraku = new RakurakuPhone('xxx-xxxx-xxxx');
rakuraku.call();
// Error
// rakuraku.call('xxx-xxxx-xxxx');

var phone = <Phone>rakuraku;
phone.call('xxx-xxxx-xxxx');
// Error
// phone.call();

不要なパラメーターを引き継ぐ必要がないのは便利。インターフェースの継承には適用されないのが勿体ない。