Declaración de Linq para una secuencia infinita de mitades sucesivas
-
29-10-2019 - |
Pregunta
Dado un número inicial, imagine una secuencia infinita de sus mitades sucesivas.
1, 0.5, 0.25, 0.125, ...
(Ignore cualquier inestabilidad numérica inherente a double
.)
¿Se puede hacer esto en una sola expresión sin escribir ningún método de extensión personalizado o métodos de generador?
Solución
No sé de una forma de expresión única, pero aquí encontré este inteligente código generador: http://csharpintepth.com/articles/chapter11/streaminganditerators.aspx
public static IEnumerable<TSource> Generate<TSource>(TSource start,
Func<TSource,TSource> step)
{
TSource current = start;
while (true)
{
yield return current;
current = step(current);
}
}
En su caso lo usaría:
foreach (double d in Generate<double>(1, c => c / 2))
{
...
}
Otros consejos
Por diversión, aquí hay un truco para crear una secuencia infinita real en una sola expresión. Las dos primeras definiciones son campos de clase, por lo que no requieren una expresión para ser inicializada.
double? helper;
IEnumerable<double> infinite;
infinite = new object[] { null }.SelectMany(dummy => new double[] { (helper = (helper / 2) ?? 1).Value }.Concat(infinite));
Aquí hay una respuesta similar a la que se proporcionó @HVD, pero usando el Y
operador definido aquí, esto elimina la necesidad de las variables locales:
public static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f)
{
return t => f(Y(f))(t);
}
var halves = Y<double, IEnumerable<double>>(self => d => new[] { 0d }.SelectMany(_ => new[] { d }.Concat(self(d / 2))));
Un uso de ejemplo sería:
foreach (var half in halves(20))
Console.WriteLine(half);
Que emitiría 20, 10, 5, 2.5 etc ...
No recomendaría usar esto en el código de producción, pero es divertido.
los Y
El operador también permite otras expresiones de lambda recursivas, por ejemplo:
var fibonacci = Y<int, int>(self => n => n > 1 ? self(n - 1) + self(n - 2) : n);
var factorial = Y<int, int>(self => n => n > 1 ? n * self(n - 1) : n);
var hanoi = Y<int, int>(self => n => n == 1 ? 1 : 2 * self(n - 1) + 1);
Enumerable.Repeat(1, int.MaxValue).Select((x, i) => x / Math.Pow(2, i))
En realidad no es infinito, sino como ambos Repeat
y Select
Use la ejecución diferida, no perderá ningún rendimiento.
No conozca ninguna forma nativa de crear una expresión infinita de Linq.
O puede escribir manualmente la versión infinita de .Repeat
No sé de ninguna forma de hacer una secuencia infinita con Linq recto. Pero podrías hacer un muy largo secuencia.
var sequence = Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(2, -n));
Sin embargo, desde double
tiene precisión finita, no obtendrás más que ceros después n
se pone demasiado alto.