##そもそもAggregateとはなんぞや
ある集合(IEnumerable<TSource>)に対して処理を通して1つの結果(TSource または TAccumulate)を受け取る集合系メソッドです。
要するに T[] があった時に T 一つひとつに何らかの処理を通して集計した結果を返す的なイメージでOK。
Haskellでいうfoldみたいな。
で、これを使う時3つほどオーバーロードがあるんですが、それぞれの挙動がちょっとだけ気になったので調べてみた。
###オーバーロードについて
public static TSource Aggregate<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TSource> func) { return /* hogemoge */ }
public static TAccumulate Aggregate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func) { return /* hogemoge */ }
public static TResult Aggregate<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
Func<TAccumulate, TResult> resultSelector) { return /* hogemoge */ }
と、3種類あります。
内容は見てわかるので割愛。気が向いた時に中身の解説するかもめ。
で、今回注目したいのは、Seedを渡す時と渡さない時の動作の違い。
ちょっと考えれば当たり前なんだけど忘れがちなので。
###まずSeedを与えない場合。
// 要素ゼロでもLINQなら。そんなふうに考えていた時期が俺にもありました。
Enumerable.Empty<int>().Select(i => i.ToString())
.Aggregate((acc, succ) => acc + succ); //=> InvalidOperationException
// string.Join みたいなことする
Enumerable.Range(1, 10).Select(i => i.ToString())
.Aggregate((acc, succ) => acc + "," + succ); //=> "1,2,3,4,5,6,7,8,9,10"
###次にSeedを与える場合。
// エラー出さない
Enumerable.Empty<int>().Select(i => i.ToString())
.Aggregate("", (acc, succ) => acc + "," + succ); //=> ""
// string.Join みたいなことしたい人生だった
Enumerable.Range(1, 10).Select(i => i.ToString())
.Aggregate("", (acc, succ) => acc + "," + succ); //=> ",1,2,3,4,5,6,7,8,9,10"
はい、エラーが出なくなりますし頭に "," がつきます。
##何が違うのか
seedを与えない場合、IEnumerableから取得できる1つ目の要素がseedとして使用されるため、
1つ目の要素が存在しない場合にエラーになります。
次にseedを与えた場合、それがseedとして使用されるため、最初の1ループ目に空文字と1つ目の要素がつながって
カンマが付与されるので先頭にカンマがつきます。
すごく単純だけど忘れがちなのでご注意ください。
おまけ。C#のSyntax Highlightがカラーパターンガン無視であまりにもひどくていらいらなどした。