如何通过将所有元素组合在一起来减少列表?

时间:2011-02-14 00:24:54

标签: c# linq

如果我没弄错的话,Linq的.Aggregate就像其他语言的reduce()一样,不是吗?

无论如何,我正在尝试这个:

var tags = new[] { "a", "b", "c", ... };
tag.Rule = tags.Aggregate((x, y) => ToTerm(x) | y);

但它不会编译,因为我的lambda的返回类型必须是一个字符串。那我怎么能让它发挥作用呢?

我知道它看起来很有趣,因为类型(这是一个反复的语法),但你真的需要知道ToTerm()会返回KeyTermKeyTerms可以或者一起制作BnfExpression,这就是tag.Rule所期待的。

3 个答案:

答案 0 :(得分:6)

您正在调用Aggregate<TSource>(IEnumerable<TSource>, Func<TSource, TSource, TSource>)重载,它使用序列中的第一个值作为初始累加器值。

如果希望累加器与序列中的元素具有不同的类型,则需要自己指定初始累加器值:

tag.Rule = tags.Aggregate(BnfExpression.Empty, (x, y) => ToTerm(x) | y);

答案 1 :(得分:2)

如果没有空/身份值的概念,Anon的解决方案将无效。

就我而言,没有。至少不是我找到的。

因此,我写了这个额外的重载:

public static TRet Aggregate<TRet, TVal>(this IEnumerable<TVal> e, Func<TVal, TRet> seed, Func<TRet, TVal, TRet> func)
{
    return e.Skip(1).Aggregate(seed(e.First()), func);
}

允许您分别处理第一个元素。你可以这样称呼它:

tagName.Rule = tags.Aggregate(first => (BnfExpression)ToTerm(first), (seed, item) => seed | item);

答案 2 :(得分:1)

如果它在视觉上更有意义,您还可以使用.Select预转换:

tag.Rule = tags
    // turn the list into our expected result type
    .Select(item => ToTerm(item))
    // now reduce
    .Aggregate( (seed,item) => seed | item );

与@ Anon使用显式种子值的答案几乎相同:

var initialSeed = default(TagRule); // or whatever it should be so logic works
tag.Rule = tags.Aggregate( initialSeed, (seed, item) => seed | ToTerm(item) );