Dichiarazione LINQ per una sequenza infinita di metà successiva
-
29-10-2019 - |
Domanda
Dato un numero iniziale, immagina una sequenza infinita delle sue metà successive.
1, 0.5, 0.25, 0.125, ...
(Ignora qualsiasi instabilità numerica inerente a double
.)
Questo può essere fatto in un'unica espressione senza scrivere metodi di estensione personalizzati o metodi del generatore?
Soluzione
Non conosco un modo a espressione singola ma ho trovato questo codice di generatore intelligente qui: http://csharpindepth.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);
}
}
Nel tuo caso lo useresti:
foreach (double d in Generate<double>(1, c => c / 2))
{
...
}
Altri suggerimenti
Per divertimento, ecco un trucco per creare una vera sequenza infinita in una singola espressione. Le prime due definizioni sono i campi di classe, in modo che non richiedano inizializzate un'espressione.
double? helper;
IEnumerable<double> infinite;
infinite = new object[] { null }.SelectMany(dummy => new double[] { (helper = (helper / 2) ?? 1).Value }.Concat(infinite));
Ecco una risposta simile a quella fornita @hvd, ma usando il Y
Operatore definito qui, questo rimuove la necessità delle variabili locali:
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 di esempio sarebbe:
foreach (var half in halves(20))
Console.WriteLine(half);
Che produrrebbe 20, 10, 5, 2,5 ecc ...
Non consiglierei di usarlo nel codice di produzione ma è divertente.
Il Y
L'operatore consente inoltre altre espressioni di lambda ricorsive, ad esempio:
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))
In realtà non è infinito, ma come entrambi Repeat
e Select
Usa l'esecuzione differita, non perderai alcuna prestazione.
Non conosco alcun modo nativo per creare un'espressione infinita LINQ.
Oppure puoi scrivere manualmente la versione infinita di .Repeat
Non conosco alcun modo per creare una sequenza infinita con Linq dritto. Ma potresti fare un molto lungo sequenza.
var sequence = Enumerable.Range(0, int.MaxValue)
.Select(n => Math.Pow(2, -n));
Tuttavia, da quando double
ha precisione finita, non otterrai altro che zero dopo n
diventa troppo alto.