シーケンスから要素を取得し合計値が指定した条件になるまで一覧を作成する拡張メソッド
いったい何を言いたいのかわからないタイトルになってしまいました。日本語難しい。 期待する結果のイメージは以下の通り。
[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
関数と Excel の SUMIF
関数が由来です。
ここからはコードが洗練されるまでの道程です。
続きを読む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)
- 作者: スティーブ・フェントン,鈴木幸敏,株式会社クイープ
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/23
- メディア: 大型本
- この商品を含むブログ (1件) を見る
クラスのメソッドでは、インターフェースで指定されているものよりも少ない数のパラメータを定義できる。それにより、そのメソッドを実行する上で必要のない引数をクラスが無視できるようになる。
とのことなので試してみた。
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();
不要なパラメーターを引き継ぐ必要がないのは便利。インターフェースの継承には適用されないのが勿体ない。