La valutazione / esecuzione pigra non dovrebbe essere sintatticamente differenziata dalla valutazione / esecuzione desiderosa in C #?

5

Sappiamo dell'esecuzione posticipata o delle funzionalità di valutazione lazy introdotte in C #. Ma a volte le persone si confondono con loro. Perché non c'è alcuna differenza significativa. Puoi solo sapere se conosci gli interni. Non dovrebbe esserci una differenza sintattica tra loro per rimuovere la confusione?

Penso che la parola chiave let di F # possa essere importata in C # per specificare le valutazioni lazy. Come -

let selectedItems = Items.Where(i => i.Count < 5);

Cosa ne pensate?

Aggiornamento:
In risposta a prima risposta , non sto proponendo che la parola chiave let debba essere utilizzata nei luoghi in cui la valutazione lazy sta già accadendo secondo il modello di compilazione attuale. Allora sarà inutile in realtà. Sto proponendo che la parola chiave let debba definire il tipo di esecuzione. Per essere chiari -

var selectedItems = Items.Where(i => i.Count < 5).ToList();

sarà eseguito con entusiasmo dal modello attuale. Ma la mia proposta è, se usiamo

let selectedItems = Items.Where(i => i.Count < 5).ToList();

sarà eseguito in seguito, solo quando richiesto. Anche se questo non è desiderabile in quanto restituirà diverse liste di oggetti identici ogni volta. La 'cosa' definita con let parola chiave può essere pensata come una costante 'espressione' come F #.

Aggiornamento 2:
Collega questa risposta SO come prova del fastidio della gente con un'esecuzione differita -

link

    
posta Gulshan 07.05.2011 - 10:40
fonte

4 risposte

12

Prima di tutto, è molto difficile dire cos'è "esecuzione differita":

User user = GetCurrentUser();
bool hasPermission = user.HasPermissionToAccessPrinter();

Questa è "esecuzione differita"? La domanda se l'utente ha l'abilità, in questo momento, di accedere alla stampante può cambiare nel tempo. Quando si crea l'oggetto "utente", è molto improbabile che faccia così delle cache nell'oggetto utente se l'utente ha il diritto di accedere alla stampante. Il lavoro per ottenere la risposta a questa domanda è probabilmente rinviato fino a quando non viene posta la domanda.

Com'è diverso da

var query = customers.Where(c=>c.City=="London");
var first = query.FirstOrDefault();

?

La stessa cosa. Il "dove" viene eseguito con entusiasmo e restituisce un oggetto che sa come rispondere alle domande, ad esempio "qual è il primo cliente che soddisfa la query?" La risposta potrebbe cambiare nel tempo in modo da non dover memorizzare nella cache il risultato quando viene creato l'oggetto query. Vuoi calcolare il risultato quando viene fatta la domanda, non prima.

Come puoi capire la differenza tra questi due casi? Penso che probabilmente non si caratterizzerebbe il primo come "esecuzione differita" ma si caratterizzerebbe il secondo come "esecuzione differita". Non sono sicuro che l'idea di "esecuzione differita" abbia senso. Tutta l'esecuzione viene posticipata finché non viene chiamato un metodo.

In secondo luogo, penso che il compilatore supporti già la funzionalità di "esecuzione differita" che si desidera. Quando dici:

int x;
let x = Foo();  // Does not call Foo() right now
M(x); // this calls Foo() and passes the result to x

è lo stesso di:

Func<int> x;
x = ()=>Foo();
M(x());  

Quindi, abbiamo già la funzione che desideri. A Func<T> è il modo in cui si rappresenta una T differita che verrà calcolata in modo sincrono su richiesta. Allo stesso modo, un Task<T> è il modo in cui si rappresenta una T differita in modo asincrono che attiverà una richiamata quando viene calcolata. E un Lazy<T> è un T differito memorizzato nella cache che viene calcolato in modo sincrono. Tutte queste funzionalità sono già nel sistema di tipi.

Potremmo sollevarli nella lingua corretta, se lo volessimo. Lo stiamo facendo con Task<T> nella prossima versione. Ma il vantaggio di farlo deve essere davvero alto; con la funzione asincrona, rende il codice molto più semplice. La funzione "let" che proponi non è molto più semplice di una semplice Func<T> e invocandola.

    
risposta data 09.05.2011 - 19:39
fonte
5

In primo luogo: non mi piace il termine "pigro". È troppo ambiguo , IMO.

In secondo luogo, affermi che queste funzionalità sono state introdotte in C # - dove, esattamente? I blocchi Iterator sono pigri in quanto il codice che scrivete in essi non viene eseguito fino a quando non si inizia a scorrere la sequenza - ma tutto ciò che si può scrivere con un blocco iteratore può essere scritto senza uno. Allo stesso modo le espressioni lambda danno la possibilità di esprimere il codice per essere eseguito in seguito - ma ancora una volta, non fanno nulla che non può essere fatto senza di loro.

In terzo luogo, affermi "puoi solo sapere se conosci gli interni" e citare Where come esempio. La documentazione per Enumerable.Where è abbastanza chiara su questo:

This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.

Questo non significa conoscere la parte interna - questo è conoscere la funzionalità documentata. Se non sai cosa fa un metodo, non hai alcun problema a chiamarlo.

Quindi a) Non vedo davvero un problema, e b) Non mi piace molto la tua soluzione proposta. Non coprirebbe tutti i casi (il codice potrebbe ancora essere facilmente scritto per dare una semantica pigra anche se usato senza let ) che causerebbe più confusione IMO.

Se voglio che qualcosa venga valutato pigramente, posso farlo usando Lazy<T> abbastanza facilmente, ed esplicitamente.

    
risposta data 07.05.2011 - 17:15
fonte
2

Se il tuo intento è quello di mostrare chiaramente che la valutazione di un valore deve essere posticipata fino al momento dell'utilizzo, puoi semplicemente usare Lazy in .NET Framework 4.

Farà la stessa cosa di let nell'esempio, ma non richiede né di aggiungere una nuova parola chiave alla lingua né di modificare il comportamento della lingua.

    
risposta data 07.05.2011 - 12:51
fonte
0

Solo let non lo taglierebbe, avresti bisogno di avere un attributo o una parola chiave lazy che sia stato applicato a tutti i metodi, proprietà e forse il campo a livello di classe che era pigro in modo che il compilatore sapesse che dovresti usa let piuttosto che var e quindi probabilmente dovresti prendere la parola per questo, o impiegare una logica piuttosto complessa per capire se la valutazione è davvero pigra. E poi per tutto quel sovraccarico e confusione in più per i programmatori di un'altra parola chiave o attributo e concetto, non sono proprio sicuro di cosa otterresti. Quindi non penso che questa sia una buona idea.

    
risposta data 07.05.2011 - 10:57
fonte

Leggi altre domande sui tag