La manera de resolver este tipo de preguntas en general, es pensar en lo que está pasando en los pasos.
Linq convierte el código linq en algo que será ejecutado por el proveedor de consultas. Esto podría ser algo como producir código SQL o todo tipo de cosas. En el caso de linq-to-objects, produce algún código .NET equivalente. Pensando en lo que será ese código .NET nos permite razonar sobre lo que sucederá *
Con su código tiene:.
Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.TakeWhile(squared => squared < 10000).Sum()
Enumerable.Range es un poco más complicado que:
for(int i = start; i != start + count; ++i)
yield return i;
... pero eso es lo suficientemente cerca como argumento.
Select es lo suficientemente cerca de:
foreach(T item in source)
yield return func(item);
¿Dónde está lo suficientemente cerca de:
foreach(T item in source)
if(func(item))
yield return item;
TakeWhile es lo suficientemente cerca de:
foreach(T item in source)
if(func(item))
yield return item;
else
yield break;
suma es lo suficientemente cerca de:
T tmp = 0;//must be numeric type
foreach(T x in source)
tmp += x;
return tmp;
Esto simplifica algunas optimizaciones y demás, pero está lo suficientemente cerca como para razonar. Teniendo cada una de ellas, a su vez, su código es equivalente a:
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()
Ahora, en cierto modo, el código anterior es más simple, y en algunos aspectos es más complicado. El objetivo de utilizar LINQ es que, en muchos aspectos, es más simple. Aún así, para responder a su pregunta "¿Este código iterará sobre todos los valores enteros de 0 a rango máximo o solo a través de los valores enteros para satisfacer los operadores take-while, where y select?" podemos ver lo anterior y ver que aquellos que no satisfacen el lugar se repiten para encontrar que no satisfacen el lugar, pero no se hace más trabajo con ellos, y una vez que el TakeWhile está satisfecho, todo el trabajo posterior está detenido (el break
en mi re-escritura no LINQ).
Por supuesto, es solo el TakeWhile()
en este caso, lo que significa que la llamada volverá en un período de tiempo razonable, pero también necesitamos pensar brevemente sobre los demás para asegurarnos de que ceden a medida que avanzan. Considere la siguiente variante de su código:
Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(n, 2))
.Where(squared => squared % 2 != 0)
.ToList()
.TakeWhile(squared => squared < 10000).Sum()
En teoría, esto le dará exactamente la misma respuesta, pero tomará mucho más tiempo y mucho más memoria para hacerlo (probablemente suficiente para provocar una excepción de memoria insuficiente). El código no LINQ equivalente aquí sin embargo es:
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()
Aquí podemos ver cómo la adición de ToList()
a la consulta LINQ afecta enormemente la consulta para que cada artículo producido por la llamada Range()
debe ser tratado. Métodos como ToList()
y ToArray()
rompen el encadenamiento de modo que los equivalentes que no son linq ya no encajen "dentro" entre sí y ninguno puede por lo tanto detener el funcionamiento de los anteriores. (Sum()
es otro ejemplo, pero como está después de su TakeWhile()
en su ejemplo, eso no es un problema).
Otra cosa que lo haría pasar por cada iteración del rango es si tenía While(x => false)
porque nunca realizaría la prueba en TakeWhile
.
* Aunque puede haber más optimizaciones, especialmente en el caso del código SQL y también, aunque conceptualmente, p. Ej. Count()
es equivalente a:
int c = 0;
foreach(item in src)
++c;
return c;
que esto se convirtió en una llamada a la propiedad Count
de un ICollection o la propiedad Length
de una matriz significa que el O (n) anteriormente se sustituye por un O (1) (para la mayoría de las implementaciones de ICollection) call, que es una ganancia masiva para grandes secuencias.
Uhhh ... superaste el rango máximo de un número entero ... ¿qué esperabas que sucediera? –