Odpowiedz

5

Tak, yield return ma formę kontynuacji. Chociaż w wielu użytecznych przypadkach Linq udostępnia operatory funkcjonalne, które pozwalają na podłączenie leniwego generatora sekwencji, tak naprawdę w C# 3 nie jest konieczne używanie tak dużej liczby yield return (z wyjątkiem dodawania własnych rozszerzeń w stylu Linq do własnych potrzeb; podłącz luki do biblioteki, np. Zip, Unfold).

W tym przykładzie zliczamy liczbę całkowitą przez brutalną siłę. Zasadniczo taki sam przykład w C# można zrobić z wbudowaną operatorów LINQ:

var factors = Enumerable.Range(2, 100) 
     .Join(Enumerable.Range(2, 100), 
       n => 1, n => 1, (i, j) => new { i, j }) 
     .First(v => v.i*v.j == 481); 

Console.WriteLine("Factors are " + factors.i + ", " + factors.j); 

Tutaj punktem wyjścia są moje dwa zaproszenia do Enumerable.Range, która jest wbudowana w Linq ale można realizować siebie jako:

IEnumerable<int> Range(int start, int stop) 
{ 
    for (int n = start; n < stop; n++) 
     yield return n; 
} 

Istnieją dwa nieparzyste parametry, n => 1, n => 1 parametry do Join. Wybieram 1 jako kluczową wartość dla Join do użycia przy dopasowywaniu elementów, stąd wszystkie kombinacje będą pasować i dlatego będę testował każdą kombinację liczb z zakresów.

Potem przekręcić parę wartości w rodzaj krotki (typ anonimowy) z:

(i, j) => new { i, j }) 

Wreszcie podnieść pierwszy taki krotki, dla których mój test jest spełnione:

.First(v => v.i*v.j == 481); 

Aktualizacja

Kod wewnątrz zaproszenia do First nie musi być jedynie krótkie wyrażenie testu. To może być dużo kodu imperatyw, który musi być „wznowiona” Jeśli test się nie powiedzie:

.First(v => 
     { 
      Console.WriteLine("Aren't lambdas powerful things?"); 

      return v.i*v.j == 481; 
     ); 

więc część programu, który potencjalnie musi zostać uruchomiony ponownie z różnymi wartościami idzie w tym lambda. Ilekroć lambda chce zrestartować się z innymi wartościami, po prostu zwraca false - odpowiednik wywołania amb bez żadnych argumentów.

+1

Nicea code - Ale on ma zupełnie inny efekt niż amb. Amb wybiera taką wartość, że cały kod nie zawodzi, a nie tylko następujące obliczenia. – Dario

+1

"Restart" musi być ograniczony do określonego kontekstu, jednak jest zaimplementowany; w szczególności byłoby głupio zrestartować cały program, włączając całą pracę wykonaną przed wprowadzeniem zmiennych amb! W mojej wersji jest to jasne, przez skierowanie zmiennych amb do sekwencji, a następnie kontekstem do ponownego uruchomienia jest lambda przekazana do Pierwszej - patrz powyższa aktualizacja, która może to uczynić jaśniejszym. Kod w lambda może być dowolnie skomplikowany. –

+0

Uzgodniony, niezły kod, ale jest to deterministyczne wdrożenie brutalnej siły. To nie jest współbieżna niedeterministyczna implementacja, która moim zdaniem to dwie kluczowe cechy implementacji operatora AMB. Na marginesie, od tego posta, Rx został zwolniony i to ma operatora Amb. –

5

To nie jest odpowiedź na Twoje pytanie, ale może Ci pomóc, czego chcesz.

amb jest używany do obliczeń niedeterministycznych. Jak być może wiesz, Prolog jest niedeterministycznym językiem używającym pojęcia unifikacji do wiązania wartości z zmiennymi (w zasadzie to, co kończy się na amb).

Istnieje implementacja tej funkcji w języku C# o nazwie YieldProlog. Jak można się domyślić, operator zysku jest ważnym warunkiem tego.

http://yieldprolog.sourceforge.net/