È mai troppo tardi per aggiungere un'altra risposta?
Ho scritto una tonnellata di codice LINQ-to-objects e sostengo che almeno in quel dominio è bene comprendere entrambe le sintassi per poter usare qualsiasi codice per un codice più semplice - che non è sempre puntuale sintassi.
Naturalmente ci sono dei momenti in cui la sintassi dei punti IS è la strada da percorrere - altri hanno fornito molti di questi casi; tuttavia, penso che le comprensioni siano state cambiate rapidamente - dato un brutto colpo, se vuoi. Quindi fornirò un campione in cui ritengo che le comprensibilità siano utili.
Ecco una soluzione per un puzzle di sostituzione delle cifre:
(soluzione scritta con LINQPad, ma può essere autonoma in un'app console)
// NO
// NO
// NO
//+NO
//===
// OK
var solutions =
from O in Enumerable.Range(1, 8) // 1-9
//.AsQueryable()
from N in Enumerable.Range(1, 8) // 1-9
where O != N
let NO = 10 * N + O
let product = 4 * NO
where product < 100
let K = product % 10
where K != O && K != N && product / 10 == O
select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
//Console.WriteLine("\nsolution expression tree\n" + solutions.Expression);
... quali output:
N = 1, O = 6, K = 4
Non male, la logica scorre linearmente e possiamo vedere che arriva con un'unica soluzione corretta. Questo puzzle è abbastanza facile da risolvere a mano: ragionamento che 3 > N
> 0 e O
> 4 * N implica 8 > = O
> = 4. Ciò significa che c'è un massimo di 10 casi da testare a mano (2 per N
-by- 5 per O
). Ho smarrito abbastanza: questo puzzle è offerto per scopi illustrativi LINQ.
Trasformazioni del compilatore
C'è molto che il compilatore fa per tradurre questo in una sintassi dei punti equivalente. Oltre alle solite clausole seconde e successive from
ottenute in% chiamateSelectMany
abbiamo% clausole% co_de che diventano chiamate let
con proiezioni, che utilizzano entrambi transparent-identifiers . Come sto per mostrare, il fatto di dover chiamare questi identificatori nella sintassi dei punti elimina la leggibilità di tale approccio.
Ho un trucco per esporre ciò che fa il compilatore nel tradurre questo codice nella sintassi dei punti. Se rimuovi il commento dalle due righe commentate sopra e lo esegui di nuovo, otterrai il seguente risultato:
N = 1, O = 6, K = 4
solution expression tree
System.Linq.Enumerable+d_b8.SelectMany(O => Range(1, 8), (O, N) => new <>f_AnonymousType02(O = O, N = N)).Where(<>h__TransparentIdentifier0 => (<>h__TransparentIdentifier0.O != <>h__TransparentIdentifier0.N)).Select(<>h__TransparentIdentifier0 => new <>f__AnonymousType1
2(<>h_TransparentIdentifier0 = <>h_TransparentIdentifier0, NO = ((10 * <>h_TransparentIdentifier0.N) + <>h_TransparentIdentifier0.O))).Select(<>h_TransparentIdentifier1 => new <>f_AnonymousType22(<>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, product = (4 * <>h__TransparentIdentifier1.NO))).Where(<>h__TransparentIdentifier2 => (<>h__TransparentIdentifier2.product < 100)).Select(<>h__TransparentIdentifier2 => new <>f__AnonymousType3
2(<>h_TransparentIdentifier2 = <>h_TransparentIdentifier2, K = (<>h_TransparentIdentifier2.product % 10))).Where(<>h_TransparentIdentifier3 => (((<>h_TransparentIdentifier3.K != <>h_TransparentIdentifier3.<>h_TransparentIdentifier2.<>h_TransparentIdentifier1.<>h_TransparentIdentifier0.O) AndAlso (<>h_TransparentIdentifier3.K != <>h_TransparentIdentifier3.<>h_TransparentIdentifier2.<>h_TransparentIdentifier1.<>h_TransparentIdentifier0.N)) AndAlso ((<>h_TransparentIdentifier3.<>h_TransparentIdentifier2.product / 10) == <>h_TransparentIdentifier3.<>h_TransparentIdentifier2.<>h_TransparentIdentifier1.<>h_TransparentIdentifier0.O))).Select(<>h_TransparentIdentifier3 => new <>f_AnonymousType4'3(N = <>h_TransparentIdentifier3.<>h_TransparentIdentifier2.<>h_TransparentIdentifier1.<>h_TransparentIdentifier0.N, O = <>h_TransparentIdentifier3.<>h_TransparentIdentifier2.<>h_TransparentIdentifier1.<>h_TransparentIdentifier0.O, K = <>h__TransparentIdentifier3.K))
Mettere ciascun operatore LINQ su una nuova riga, traducendo gli identificatori "indicibili" con quelli che possiamo "parlare", cambiando i tipi anonimi nella loro forma familiare e modificando il gergo di espressioni Select
in AndAlso
espone il le trasformazioni che il compilatore fa per arrivare ad un equivalente in dot-syntax:
var solutions =
Enumerable.Range(1,8) // from O in Enumerable.Range(1,8)
.SelectMany(O => Enumerable.Range(1, 8), (O, N) => new { O = O, N = N }) // from N in Enumerable.Range(1,8)
.Where(temp0 => temp0.O != temp0.N) // where O != N
.Select(temp0 => new { temp0 = temp0, NO = 10 * temp0.N + temp0.O }) // let NO = 10 * N + O
.Select(temp1 => new { temp1 = temp1, product = 4 * temp1.NO }) // let product = 4 * NO
.Where(temp2 => temp2.product < 100) // where product < 100
.Select(temp2 => new { temp2 = temp2, K = temp2.product % 10 }) // let K = product % 10
.Where(temp3 => temp3.K != temp3.temp2.temp1.temp0.O && temp3.K != temp3.temp2.temp1.temp0.N && temp3.temp2.product / 10 == temp3.temp2.temp1.temp0.O)
// where K != O && K != N && product / 10 == O
.Select(temp3 => new { N = temp3.temp2.temp1.temp0.N, O = temp3.temp2.temp1.temp0.O, K = temp3.K });
// select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
Che se corri puoi verificare che emetta nuovamente:
N = 1, O = 6, K = 4
... ma scriverebbe mai un codice come questo?
Scommetto che la risposta è NONBHN (Not Only No, But Hell No!) - perché è troppo complessa. Certo, puoi trovare nomi di identificativi più significativi di "temp0" .. "temp3", ma il punto è che non aggiungono nulla al codice - non rendono il codice più performante, non lo fanno fai in modo che il codice legga meglio, solo il codice è brutto, e se lo facessi a mano, senza dubbio lo rovinerebbero una volta o tre prima di farlo bene. Inoltre, giocare a "il gioco del nome" è abbastanza difficile per gli identificatori significativi, quindi accolgo con favore la pausa dal gioco del nome che il compilatore mi fornisce nella comprensione delle query.
Questo esempio di rompicapo potrebbe non essere reale abbastanza da poter essere preso sul serio; tuttavia, esistono altri scenari in cui le query di comprensione brillano:
- La complessità di
&&
e Join
: l'ambito delle variabili di intervallo nelle query di comprensione di query GroupJoin
converte errori che potrebbero altrimenti compilare in dot-syntax in errori di compilazione nella sintassi di comprensione.
- Ogni volta che il compilatore introduce un identificatore trasparente nella trasformazione della comprensione, le comprensioni diventano utili. Ciò include l'uso di una delle seguenti opzioni: più% clausole
join
, from
& % clausole% enunciati e clausole join
.
Conosco più di un negozio di ingegneria nella mia città natale che ha una sintassi di comprensione fuorilegge . Penso che questo sia un peccato perché la sintassi della comprensione è solo uno strumento e utile a questo. Penso che sia un po 'come dire, "Ci sono cose che puoi fare con un cacciavite che non puoi fare con uno scalpello. Perché puoi usare un cacciavite come scalpello, gli scalpelli sono vietati d'ora in poi sotto il decreto del re . "