2010-07-08 12 views
8

Mam następujące funkcje zdefiniowane:Dlaczego to stwierdzenie Haskella nie jest leniwie oceniane?

ex 1 x = 1 
ex 0 x = 0 
ex b x = b ** x 

Wtedy, kiedy wykonać następujące czynności:

1 `ex` (sum [1..]) 

próbuje obliczyć sumę nieskończonej sekwencji, zamiast być leniwy i powrotu 1. Czemu?


EDIT: Po dalszych badaniach, stwierdziliśmy, że lenistwo stanie, jeśli zdefiniować funkcję w pliku ex, ale jeśli nie mogę zdefiniować ją w GHCI:

$ ghci 
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> let ex 1 x = 1 
Prelude> let ex b x = b ** x 
Prelude> ex 1 (sum [1..]) 
<interactive>: out of memory (requested 1048576 bytes) 

Gdybym ciągnąć definicję ex do pliku (w tym przypadku test.hs):

$ ghci 
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> :load test.hs 
[1 of 1] Compiling Main    (test.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> ex 1 (sum [1..]) 
1.0 

Nowe pytanie brzmi: dlaczego?

+1

Nie, 1 'ex' (suma [1 ..]) zwraca 1.0 na moim komputerze. Jaką wersję ghc itp? Czy otrzymujesz taki sam wynik z ex 1 (suma [1 ..])? – ShreevatsaR

+1

Prawdopodobnie błąd? –

+0

@ShreevatsaR: Dobrze, zobacz moją aktualizację powyżej. – perimosocordiae

Odpowiedz

17

W GHCi, każda instrukcja let wprowadza nowy definicję ex, zamiast wielu przypadkach wzór jak można się spodziewać. Zawiesza się, ponieważ po wprowadzeniu ex 1 (sum [1..]) istnieje tylko ostatnia wersja ex b x = b ** x.

Jeśli chcesz zdefiniować funkcję z wielu przypadkach wzór w GHCi, trzeba go umieścić w jednym let oświadczeniu tak:

let ex 1 x = 1; ex 0 x = 0; ex b x = b ** x 

To samo odnosi się do niczego innego, które normalnie być zapisane w wielu liniach, na przykład w notacji do. Na przykład, funkcja tak:

f x = do 
    y <- get 
    put (x + y) 
    return y 

musiałby być napisany tak w GHCi:

let f x = do { y <- get; put (x + y); return y } 
+0

Aha, to naprawdę niewygodne. Dobrze wiedzieć, dzięki! – perimosocordiae

+6

@perimosocordiae: Jako dość ciężki użytkownik GHCi, ogólnie zacząłem pisać większość lub wszystkie moje definicje w zewnętrznym pliku, ładowanie pliku z GHCi i używanie REPL głównie do oceny prostych wyrażeń. –

+2

Możesz również użyć ': set + m' do zapisywania definicji w wielu liniach. – aleator

-2

Przeoczyłem jeden punkt lenistwa, który powoduje, że odpowiedź poniżej jest fałszywa.


Ponieważ sum oblicza łączną sumę wszystkich elementów w sekwencji. Który w twoim przypadku jest nieskończony.

Prawdopodobnie chcesz

map ((curry ex) 1) [1..] 

To

map -- map each item x to y 
    (
     (
      curry ex -- curry ex, to transform (x, y) -> z into x -> y -> z 
     ) 
     1 -- then invoke it with 1, which results in y -> z, x being 1 
    ) 
    [1..] -- the infinite sequence to be mapped. 
+0

Hm, nie sądzę, że to jest to. Zobacz moją aktualizację powyżej. – perimosocordiae

+0

Cholerne aktualizacje. Tak, dość oczywiste, o co chodzi. – Dykam

+0

-1: Obliczałoby to tylko sumę, gdyby była rzeczywiście potrzebna, aw tym przypadku tak nie było. – Zifre

1
Prelude> let ex 1 x = 1 
Prelude> let ex b x = b ** x 

Nie jesteś definiowanie funkcji z dwóch przypadków tutaj. Użytkownik definiuje funkcję w jednym przypadku, a następnie definiuje ją ponownie, zastępując poprzednią definicję.

Aby zdefiniować jedną funkcję z dwoma wzorami, należy użyć let ex 1 x = 1; ex b x = b ** x, tj. Oddzielić przypadki średnikiem.

Powiązane problemy