Come semplice dimostrazione dell'efficienza dello stile Haskell, ho eseguito senza pensieri quanto segue:
take 100 [(a, b, c) | a <- [1..], b <- [1..], c <- [1..], a^2 + b^2 == c^2]
Questo dovrebbe essere un modo per ottenere i primi 100 tripli pitagorici, con duplicati. In pratica, tuttavia, non si ferma mai, perché l'algoritmo stesso sfida la valutazione pigra.
Per pensarci in termini di effettiva implementazione, il seguente dovrebbe essere qualcosa di simile a come viene effettivamente valutata la comprensione delle liste, in uno stile imperativo:
results = []
for (a = 0; a < ∞; a++) {
for (b = 0; b < ∞; b++) {
for (c = 0; c < ∞; c++) {
if (a^2 + b^2 == c^2) {
results[] = [a, b, c]
}
}
}
}
Quando viene scritto in questo modo, diventa ovvio che la funzione non può mai produrre risultati, perché verrà speso un tempo infinito per verificare se 1^2 + 1^1 == c^2
, poiché solo il ciclo% più interno di% di transizione avanzerà e for
e a
rimarrà '1'.
La soluzione comune in questo caso particolare consiste nel vincolare i valori delle più piccole due variabili a quelle del più grande:
b
take 100 [(a, b, c) | c <- [1..],
a <- [1..c], b <- [1..c],
Tuttavia, questo sembra un evidente svista per gli implementatori della lingua. Quando ci pensi, la qualsiasi lista di comprensione con più di una fonte infinita di spazi di ricerca non si fermerà mai, per lo stesso motivo, tranne alcuni produrranno risultati utili quando (1, 1, x) è utile . Ci sono domande discussing questo problema , ma la maggior parte discute casi specifici, piuttosto che il problema generale. Perché non risolvere questo problema all'interno della lingua con un diverso schema di iterazione banale?