すもぎのめも

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

Tomas Petricek 氏と邂逅した

図らずも松竹スクエアのエレベーターで Tomas Petricek 氏、Evelina Gabasova 氏と 3人になってしまったときの記憶。

Tomas : Hello.
Evelina : Hello.
Me : Hello, I'm honor to meet you! (準備しておいた完璧な挨拶)
Tomas : Do you use F#?
Me : Sometimes. I useally use C#. 
Me : (間が持たないので一方的に喋る) F#, It's a game changer for me.
Tomas : Wao
Me : So I repeated learning F# over and over again, but I'm still level-0 Ninja.
Homas : Haha
Me : Real-World Functional Programming book is very interesting.
Will you write 2nd edition?

Tomas : Ah~ (ここからちゃんと聞き取れず。多分いろいろやってるみたいなことを言っていた)
Tomas : (こいつ聞き取れてないな、みたいな顔) Maybe.
Me : Maybe.
Me : (None だな)

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

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

[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?())

とする。