2012-02-22 9 views
7

Wyobraź sobie nieskończoną sekwencję kolejnych połówek.Instrukcja Linq dla nieskończonej sekwencji kolejnych połówek

1, 0.5, 0.25, 0.125, ... 

(Ignoruj ​​wszelkie niestabilności numerycznych nieodłączne w double).

Można to zrobić w jednym wyrażeniem bez pisania jakichkolwiek metod niestandardowych rozszerzeń lub metod generatora?

+4

Dlaczego arbitralne ograniczenie? Czy to zadanie domowe? Jeśli nie jesteś zaangażowany w wyrażane ograniczenia, po prostu napisz iterator za pomocą zwrotu 'yield'' return'. –

+5

Infinite Sequences and Computers nie działają razem. –

+6

@Ramhound Oczywiście, że tak, o ile nie starasz się uzyskać wszystkich przedmiotów. – hvd

Odpowiedz

10

Nie wiem o sposób pojedynczej wypowiedzi, ale uważam, że ten sprytny Code Generator tutaj: 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); 
    } 
} 

W twoim przypadku chcesz go używać:

foreach (double d in Generate<double>(1, c => c/2)) 
{ 
    ... 
} 
2
Enumerable.Repeat(1, int.MaxValue).Select((x, i) => x/Math.Pow(2, i)) 

W rzeczywistości nie jest nieskończony, ale ponieważ zarówno Repeat, jak i używa odroczonego wykonywania, nie stracisz żadnej wydajności.

Nie znam żadnego natywnego sposobu tworzenia nieskończonego wyrażenia linq.

Albo można ręcznie napisać nieskończoną wersję .Repeat

10

dla zabawy, tu jest podstęp, aby stworzyć prawdziwe nieskończoną sekwencję w jednym wyrażeniu. Dwie pierwsze definicje to pola klasy, które nie wymagają inicjowania wyrażenia.

double? helper; 
IEnumerable<double> infinite; 

infinite = new object[] { null }.SelectMany(dummy => new double[] { (helper = (helper/2) ?? 1).Value }.Concat(infinite)); 
+1

To wspaniale, w okropny sposób :) –

+1

Nie zamierzałem tego jak nic innego :) – hvd

+2

Fajnie. Możesz to wyliczyć w LinqPadzie, ale z jakiegoś powodu zawiesza się, jeśli spróbujesz zrobić Count() na nim; o) –

0

Nie wiem, w jaki sposób wykonać nieskończoną sekwencję z prostym LINQ. Można jednak wykonać bardzo długą sekwencję .

var sequence = Enumerable.Range(0, int.MaxValue) 
         .Select(n => Math.Pow(2, -n)); 

Jednak od double ma skończoną precyzję, prawdopodobnie będziesz dostać nic poza zerami po n staje się zbyt wysoka. Będziesz musiał poeksperymentować, aby zobaczyć, co się stanie i jak wysoko może się przedostać n.

3

Oto odpowiedź podobna do jednej @hvd dostarczonych, ale stosując operator Y zdefiniowane here ta eliminuje potrzebę zmiennych lokalnych:

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)))); 

Przykładem stosowanie byłoby:

foreach (var half in halves(20)) 
    Console.WriteLine(half); 

Który byłby wyjście 20, 10, 5, 2.5 itd. ...

Nie zaleca się używania tego w kodzie produkcji, ale jest zabawne.

Operator Y pozwala również inne rekurencyjnych wyrażeń lambda, np:

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); 
+0

To jest miłe. Używa dwóch wyrażeń, ale jest możliwe, że funkcja "Y" jest już zdefiniowana gdzieś, w takim przypadku możesz odwołać się do niej i uniknąć jej definicji. – hvd