Ho scritto due versioni del problema di nqueens e penso che dovrebbero avere un'efficienza simile ma non è così. Penso che sia dovuto al comportamento di valutazione pigro di Haskell. Qualcuno può spiegare come funziona per il seguente esempio,Ho bisogno di aiuto per comprendere il comportamento di valutazione pigro di Haskell
nqueens1 n 1 = [[i] | i <- [1..n]]
nqueens1 n k = [ i:q | i <- [1..n], q <- (nqueens1 n (k - 1)), isSafe i q k]
isSafe i q n = isSafeHelper i (zip q [(n-1),(n-2)..1])
where isSafeHelper i [] = True
isSafeHelper i (x:xs) = (i /= fst x) && abs(i - (fst x)) /= abs(n - (snd x)) &&
isSafeHelper i xs
nqueens2 n 1 = [[i] | i <- [1..n]]
nqueens2 n k = [ i:q | i <- [1..n], q <- boards, isSafe i q k]
where boards = nqueens2 n (k-1)
Si possono valutare chiamando nqueens1 8 8 8 8 o nqueens2 per valutare per un consiglio di formato 8.
Mentre nqueens2 funziona abbastanza efficientemente nqueens1 ha problemi di prestazioni. Credo che sia perché la chiamata ricorsiva (nqueens n (k-1)) viene valutata più volte. Dalla mia comprensione della valutazione pigra di Haskells non dovrebbe essere così.
Per favore aiutami a capire questo comportamento.
Grazie in anticipo.
"valutazione pigro" è di circa valutare le cose in ritardo - non si tratta di evitare che valutano qualcosa molte volte. –
@DanielWagner In realtà la differenza tra la valutazione lazy e call-by-name è esattamente che determinate espressioni che sarebbero valutate più volte utilizzando call-by-name vengono valutate solo una volta utilizzando la valutazione lazy. Questo non è legato al problema qui però. – sepp2k
@ sepp2k Hai ragione, avrei dovuto essere più preciso, dicendo "call-by-name" invece di "lazy evaluation" o dicendo "memoization" invece di "evitare di valutare qualcosa molte volte". –