一般来说,解决这类问题的方法就是想一步一步来看看发生了什么。
Linq将linq代码转换为将由查询提供程序执行的内容。这可能类似于生成SQL代码或所有方式的东西。在linq-to-objects的情况下,它会产生一些等效的.NET代码。关于什么的.NET代码会思考让我们有理由对会发生什么*
随着你的代码有:
Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.TakeWhile(squared => squared < 10000).Sum()
Enumerable.Range略大于更复杂:
for(int i = start; i != start + count; ++i)
yield return i;
...但是为了论证的缘故,这已经足够接近了。
选择是足够接近:
foreach(T item in source)
yield return func(item);
哪里是足够接近:
foreach(T item in source)
if(func(item))
yield return item;
TakeWhile足够接近:
foreach(T item in source)
if(func(item))
yield return item;
else
yield break;
总和足够接近:
T tmp = 0;//must be numeric type
foreach(T x in source)
tmp += x;
return tmp;
这简化了一些优化等等,但足够接近的原因。以每个这些反过来,你的代码就相当于:
double ret = 0; // part of equivalent of sum
for(int i = 0; i != int.MaxValue; ++i) // equivalent of Range
{
double j = Math.Pow(i, 2); // equivalent of Select(n => Math.Pow(n, 2))
if(j % 2 != 0) //equivalent of Where(squared => squared %2 != 0)
{
if(j < 10000) //equivalent of TakeWhile(squared => squared < 10000)
{
ret += j; //equaivalent of Sum()
}
else //TakeWhile stopping further iteration
{
break;
}
}
}
return ret; //end of equivalent of Sum()
现在,在某些方面,上面的代码更简单,而且在某些方面它更复杂。使用LINQ的重点在很多方面都比较简单。仍然,回答你的问题:“这个代码是否会迭代所有从0到最大范围的整数值,或者只是通过整数值来满足take-while,where和select操作符?”我们可以看看上面的内容,看看那些不满足在哪里被迭代的人发现他们不满足他们的地方,但没有更多的工作与他们完成,一旦TakeWhile满意,所有进一步的工作被停止了(我的非LINQ重写中的break
)。
当然这只是TakeWhile()
在这种情况下,这意味着电话会在合理的时间内返回,但我们也需要简单思考其他电话以确保它们随时走完。考虑你的代码的以下变型:
Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.ToList()
.TakeWhile(squared => squared < 10000).Sum()
从理论上讲,这会给一模一样的答案,但它会需要更长的时间和更内存这样做(可能足以导致内存溢出异常)。这里虽然等效的非LINQ代码:
List<double> tmpList = new List<double>(); // part of ToList equivalent
for(int i = 0; i != int.MaxValue; ++i) // equivalent of Range
{
double j = Math.Pow(i, 2); // equivalent of Select(n => Math.Pow(n, 2))
if(j % 2 != 0) //equivalent of Where(squared => squared %2 != 0)
{
tmpList.Add(j);//part of equivalent to ToList()
}
}
double ret = 0; // part of equivalent of sum
foreach(double k in tmpList)
{
if(k < 10000) //equivalent of TakeWhile(squared => squared < 10000)
{
ret += k; //equaivalent of Sum()
}
else //TakeWhile stopping further iteration
{
break;
}
}
return ret; //end of equivalent of Sum()
在这里我们可以看到如何添加ToList()
到LINQ查询极大地影响了查询,以便由Range()
调用生成每个项目必须处理。像ToList()
和ToArray()
这样的方法打破了链接,因此非linq等价物不再适合彼此“内部”,因此没有一个可以停止那些之前的操作。 (Sum()
是另一个例子,但是因为它在你的例子中是TakeWhile()
之后的,所以这不是问题)。
如果你有While(x => false)
因为它实际上永远不会在TakeWhile
中执行测试,那么它将会经历范围的每次迭代。 *尽管可能会有进一步的优化,尤其是在SQL代码的情况下,并且在概念上也是如此。 Count()
相当于:
int c = 0;
foreach(item in src)
++c;
return c;
,这将变成一个呼叫到一个ICollection的或阵列的Length
属性的Count
属性是指O(n)的上述由O替代(1)(对于大多数ICollection实现来说),这对于大型序列来说是一个巨大的收益。
呃......你超过了整数的最大范围......你期望会发生什么? – 2011-03-26 17:25:20