mam tego dość prostą funkcję, aby obliczyć średnią z elementów dużego listy, przy użyciu dwóch akumulatorów do przechowywania sumę dotychczas i liczyć do tej pory:Lenistwo i rekurencja ogona w Haskell, dlaczego to się zawiesza?
mean = go 0 0
where
go s l [] = s/fromIntegral l
go s l (x:xs) = go (s+x) (l+1) xs
main = do
putStrLn (show (mean [0..10000000]))
Teraz w ścisłym języku, byłoby być rekursywnym rekwizytem i nie byłoby problemu. Jednakże, jak Haskell jest leniwy, moje googlowanie doprowadziło mnie do zrozumienia, że (s + x) i (l + 1) zostaną przekazane w rekurencji jako strzały. Więc cała ta sprawa i wywala oparzenia:
Stack space overflow: current size 8388608 bytes.
Po dalszym googlowania znalazłem seq
i $!
. Co wydaje mi się, że nie rozumiem, ponieważ wszystkie moje próby wykorzystania ich w tym kontekście okazały się daremne, a komunikaty o błędach mówią coś o nieskończonych typach.
Wreszcie znalazłem -XBangPatterns
, który rozwiązuje to wszystko przez zmianę wywołanie rekurencyjne:
go !s !l (x:xs) = go (s+x) (l+1) xs
Ale nie jestem zadowolony z tego, jak -XBangPatterns
jest obecnie rozszerzenie. Chciałbym wiedzieć, jak dokonać oceny ścisłej bez korzystania z -XBangPatterns
. (! A może nauczyć się czegoś zbyt)
Wystarczy więc zrozumieć mój brak zrozumienia, oto co próbowałem (starając się, że skompilowany tylko, że jest):
go s l (x:xs) = go (seq s (s+x)) (seq l (l+1)) xs
Z tego co rozumiem, nast powinien tu wymusić ocenę argumentów s i l, unikając w ten sposób problemu spowodowanego przez tony. Ale wciąż dostaję przepełnienie stosu.
Dobre rzeczy. Miło wiedzieć o optymalizacji GHC, a dzięki za link do książki wygląda jak świetny zasób. Jednak gdy spojrzałem na posta tego, uderzyło mnie, że to wygląda na to, że użycie seq powinno przerwać rekursję ogona.seq musi zostać oceniony po zakończeniu rekurencyjnego wywołania, aby przejść, został oceniony , więc z mojej wiedzy na temat rekurencji ogona, powinien on być już nie rekurencyjny, a więc wysadzić stos. Ten kurs się nie dzieje, więc coś tu się dzieje. Czy Haskell traktuje seq specjalnie? A może po prostu jestem zdezorientowany, jeśli chodzi o rekurencję ogona ? – Hisnessness
seq nie istnieje w czasie wykonywania. To tylko wskazówka, aby użyć innej strategii oceny. Otrzymasz całkowicie inny wygenerowany kod. Pomyśl o tym bardziej jak {- # STRICT_WHNF # -} pragma –