risposta

5

Sì, yield return fa una forma di continuazione. Sebbene per molti casi utili, Linq fornisce operatori funzionali che consentono di collegare un generatore di sequenza pigro, quindi in C# 3 non è necessario utilizzare yield return così tanto (tranne quando si aggiungono più estensioni in stile Linq per tappare le lacune nella libreria, ad esempio Zip, Unfold).

Nell'esempio viene calcolato un numero in base alla forza bruta. In sostanza lo stesso esempio in C# può essere fatto con il built-in operatori 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); 

Qui i punti di partenza sono i miei due chiamate a Enumerable.Range, che è built-in per Linq ma si potrebbe implementare te stesso come:

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

ci sono due parametri dispari, il n => 1, n => 1 parametri Join. Sto selezionando 1 come valore chiave per Join da utilizzare quando si abbinano gli articoli, quindi tutte le combinazioni corrisponderanno e così potrò testare ogni combinazione di numeri dagli intervalli.

Poi giro la coppia di valori in una sorta di tupla (un tipo anonimo) con:

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

Infine, prendo il primo tale tupla per cui è soddisfatto il mio test:

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

Aggiornamento

il codice all'interno della chiamata a First non bisogna essere solo una breve espressione di prova. Può essere un bel po 'di codice imperativo che deve essere "riavviato" se il test fallisce:

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

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

Quindi la parte del programma che ha bisogno potenzialmente essere riavviato con valori diversi va in quel lambda. Ogni volta che lambda vuole riavviarsi con valori diversi, restituisce semplicemente false - l'equivalente di chiamare amb senza argomenti.

+1

Nizza codice - Ma ha un effetto molto diverso da amb. Amb sceglie il suo valore in modo tale che l'intero codice non fallisca, non solo il seguente calcolo. – Dario

+1

Il "riavvio" deve essere limitato a un particolare contesto, tuttavia è implementato; nello specifico, sarebbe sciocco riavviare l'intero programma includendo tutto il lavoro svolto prima dell'introduzione delle variabili amb! Nella mia versione, ciò è reso chiaro canalizzando le variabili amb in una sequenza, e quindi il contesto riavviabile è il lambda passato a Primo: vedere l'aggiornamento sopra che può rendere più chiaro questo. Il codice nel lambda può essere arbitrariamente complicato. –

+0

Concordato, bel codice, ma questa è un'implementazione di forza bruta deterministica. Questa non è un'implementazione concomitante non deterministica, che credo siano due degli attributi chiave dell'implementazione di un operatore AMB. Come nota a margine, dal momento che questo post, Rx è stato rilasciato e che ha un operatore Amb. –

5

Questa non è una risposta alla tua domanda, ma potrebbe farti ottenere ciò che desideri.

amb è utilizzato per il calcolo non deterministico. Come forse sapete, Prolog è un linguaggio non deterministico che usa la nozione di unificazione per legare valori a variabili (in pratica ciò che amb finisce per fare).

Esiste un'implementazione di questa funzionalità in C#, denominata YieldProlog. Come hai intuito, l'operatore di rendimento è un requisito importante per questo.

http://yieldprolog.sourceforge.net/

Problemi correlati