【发布时间】:2010-07-01 15:43:31
【问题描述】:
任何人都可以解释为什么下面的第三个查询比其他查询慢几个数量级,而它不应该比按顺序执行前两个查询花费更长的时间吗?
var data = Enumerable.Range(0, 10000).Select(x => new { Index = x, Value = x + " is the magic number"}).ToList();
var test1 = data.Select(x => new { Original = x, Match = data.Single(y => y.Value == x.Value) }).Take(1).Dump();
var test2 = data.Select(x => new { Original = x, Match = data.Single(z => z.Index == x.Index) }).Take(1).Dump();
var test3 = data.Select(x => new { Original = x, Match = data.Single(z => z.Index == data.Single(y => y.Value == x.Value).Index) }).Take(1).Dump();
编辑:我已将 .ToList() 添加到原始数据生成中,因为我不希望任何重复生成的数据使问题变得模糊。
我只是想了解为什么顺便说一句,这段代码如此缓慢,而不是寻找更快的替代方案,除非它对此事有所启发。我原以为如果 Linq 被懒惰地评估并且我只寻找第一项 (Take(1)) 那么 test3 的:
data.Select(x => new { Original = x, Match = data.Single(z => z.Index == data.Single(y => y.Value == x.Value).Index) }).Take(1);
可以简化为:
data.Select(x => new { Original = x, Match = data.Single(z => z.Index == 1) }).Take(1)
在 O(N) 中,因为在内部 Single() 对数据进行一次完整扫描后,数据中的第一项成功匹配,剩下的 Single() 对数据进行了一次扫描。所以仍然是 O(N)。
显然它正在以更冗长的方式处理,但我真的不明白如何或为什么。
顺便说一句,Test3 需要几秒钟的时间才能运行,所以我认为我们可以有把握地假设,如果你的答案包含数字 10^16,那么你在某个地方犯了一个错误。
【问题讨论】:
-
可能是因为它必须做更多数量级的工作。你为什么认为它不应该再花更多时间?
-
@Jay:他一定在使用 LINQPad。这是一种将结果转储到结果窗格的内置方法。
-
嗨,杰。 Dump 只是一个将其发送到控制台的 LinqPad 扩展方法。
-
您可以将
Dump()替换为ToList()并观察减速,无需 LINQPad。 -
我应该为自己嘲笑 Take(1).Dump() 而感到羞耻吗?
标签: linq