Napisałem dwie wersje problemu nasek i myślę, że powinny one mieć podobną wydajność, ale tak nie jest. Myślę, że wynika to z leniwego zachowania ewaluacyjnego Haskella. Może ktoś proszę wyjaśnić, jak to działa na poniższym przykładziePotrzebujesz pomocy w zrozumieniu leniwego zachowania ewaluacyjnego Haskella
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)
można je ocenić przez wywołanie nqueens1 8 8 8 8 lub nqueens2 ocenić go na płytce o wymiarach 8.
Podczas nqueens2 działa całkiem skutecznie nqueens1 ma problemy z wydajnością. Wierzę, że jest tak, ponieważ wywołanie rekursywne (nqueens n (k-1)) jest oceniane wielokrotnie. Z mojego zrozumienia lenistwa Haskella wynika, że tak nie powinno być.
Proszę mi pomóc w zrozumieniu tego problemu.
Z góry dziękuję.
„lazy ocenę” jest o ocenę rzeczy późno - nie chodzi o unikanie oceniające coś wiele razy. –
@DanielWagner Faktycznie, różnica między leniwym oszacowaniem a wywołaniem nazwy jest dokładnie taka, że niektóre wyrażenia, które byłyby wielokrotnie oceniane przy użyciu nazwy wywołania, są oceniane tylko raz przy użyciu leniwej oceny. Nie ma to jednak związku z problemem. – sepp2k
@ sepp2k Masz rację, powinienem być bardziej precyzyjny, mówiąc "call-by-name" zamiast "leniwej oceny" lub mówiąc "memoization" zamiast "unikając oceniania czegoś wiele razy". –