How can I reduce a list by or-ing all its elements together?
If I'm not mistaken, Linq's .Aggregate
works like reduce()
in other languages, doesn't it?
Anyway, I'm trying this:
var tags = new[] { "a", "b", "c", ... };
tag.Rule = tags.Aggregate((x, y) => T开发者_运维百科oTerm(x) | y);
But it won't compile because the return type of my lambda has to be a string. So how can I get this to work?
I know it looks funny, because of the types (this is going into an Irony grammar), but all you really need to know that ToTerm()
returns a KeyTerm
, and KeyTerms
can be or'd together to make a BnfExpression
, which is what tag.Rule is expecting.
You're calling the Aggregate<TSource>(IEnumerable<TSource>, Func<TSource, TSource, TSource>)
overload, which uses the first value in the sequence as the initial accumulator value.
If you want the accumulator to have a different type from the elements in the sequence, you'll need to specify the initial accumulator value yourself:
tag.Rule = tags.Aggregate(BnfExpression.Empty, (x, y) => ToTerm(x) | y);
Anon's solution won't work if there's no concept of an empty/identity value.
In my case, there isn't. At least not that I've found.
Thus, I've written this extra overload:
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);
}
Which allows you to process the first element separately. You can call it like this:
tagName.Rule = tags.Aggregate(first => (BnfExpression)ToTerm(first), (seed, item) => seed | item);
If it makes more sense visually, you can also pre-convert with .Select
:
tag.Rule = tags
// turn the list into our expected result type
.Select(item => ToTerm(item))
// now reduce
.Aggregate( (seed,item) => seed | item );
Which is pretty much the same thing as @Anon's answer using an explicit seed value:
var initialSeed = default(TagRule); // or whatever it should be so logic works
tag.Rule = tags.Aggregate( initialSeed, (seed, item) => seed | ToTerm(item) );
精彩评论