我知道,如果我在同一条语句中将&&
或||
运算符链接到一起,c#将停止计算该语句,并在计算表达式时返回适当的结果,并且无论下面的表达式是什么,结果都不会改变。例如:
var result = false && foo() && bar();
在此语句中,foo()
和bar()
将永远不会执行,因为第一个表达式为false。我的问题是,当运行一组bool时,Enumerable.Aggregate<TSource, TAccumulate>
会做同样的事情,还是会计算所有的表达式?例如:
var result = new List<bool>
{
false,
foo(),
bar()
}.Aggregate(true, (acc, x) => acc && x);
发布于 2017-07-01 00:40:49
是的,您示例中的&&
操作符将会短路。当acc
为false
时,不会对x
求值。
当然,每个元素都需要执行整个谓词。Aggregate
不可能知道以后执行它不会改变聚合值。
聚合函数实际上是在数据集上执行All
操作,如果使用All
操作,而不是更通用的Aggregate
操作,那么只要给定的谓词为false
,它就能够确定结果是已知的且不能更改,并且不需要在其余元素上调用该谓词(当找到true
值时,Or
也是如此)。
发布于 2017-07-01 01:34:57
正如其他答案所指出的,您的聚合调用将遍历整个集合,但是每个调用上的&&将短路。这里有一个例子可以说明这一点:
您可以看到,在result1中,一旦得到错误的结果,它就会停止计算x()。这是由于acc短路造成的。
在result2中,我们将acc移到了&&的右边,它强制在所有情况下都对x()求值。
Console.WriteLine("acc && x()");
var result1 = new List<Func<bool>>
{
() => {Console.WriteLine("One"); return true;},
() => {Console.WriteLine("Two"); return false;},
() => {Console.WriteLine("Three"); return false;}
}.Aggregate(true, (acc, x) => acc && x());
Console.WriteLine();
Console.WriteLine("x() && acc");
var result2 = new List<Func<bool>>
{
() => {Console.WriteLine("One"); return true;},
() => {Console.WriteLine("Two"); return false;},
() => {Console.WriteLine("Three"); return false;}
}.Aggregate(true, (acc, x) => x() && acc);
输出:
acc && x()
One
Two
x() && acc
One
Two
Three
发布于 2018-06-13 07:19:35
根据另一个问题,我正在使用Aggregate
,如果出现某个中间值,则需要提前退出,这最终涉及到许多三元运算符(或带有if
的λ主体),以便在达到该值时绕过聚合,因此我编写了一些新的扩展以提前退出:
public static T AggregateWhile<T>(this IEnumerable<T> src, Func<T, T, T> accumFn, Predicate<T> whileFn) {
using (var e = src.GetEnumerator()) {
if (!e.MoveNext())
throw new Exception("At least one element required by AggregateWhile");
var ans = e.Current;
while (whileFn(ans) && e.MoveNext())
ans = accumFn(ans, e.Current);
return ans;
}
}
public static TAccum AggregateWhile<TAccum, T>(this IEnumerable<T> src, TAccum seed, Func<TAccum, T, TAccum> accumFn, Predicate<TAccum> whileFn) {
using (var e = src.GetEnumerator()) {
if (!e.MoveNext())
throw new Exception("At least one element required by AggregateWhile");
var ans = accumFn(seed, e.Current);
while (whileFn(ans) && e.MoveNext())
ans = accumFn(ans, e.Current);
return ans;
}
}
对于您的示例,您可以使用它,如下所示:
var result = new List<bool> {
false,
foo(),
bar()
}.AggregateWhile(true, (acc, x) => acc && x, acc => acc);
当然,foo()
和bar()
会被执行,因为List<bool>
不会延迟执行,所以只节省了循环时间。但是如果你有一个这样的序列:
var result = new List<Func<bool>> { () => false, () => foo(), () => bar() }
.AggregateWhile(true, (acc, f) => acc && f(), acc => acc);
这将跳过执行foo
和bar
。
作为练习,留下了明显的对应AggregateUntil
:)
https://stackoverflow.com/questions/44851218
复制相似问题