2015-03-31 2 views
28

Ich brauche etwas Klärung über Faulheit mit Haskell.Inwiefern ist Haskell faul?

Wenn ich diese Funktion:

myFunction arg 
    | arg == 1 = a 
    | arg == 2 = a*b 
    | arg == 3 = b+c 
    | otherwise = (a+b)*c 
    where 
      a = ... 
      b = ... 
      c = ... 
      d = ... 

Als ich myFunction 1 nennen, werden Haskell bewerten nur die a = ... Funktion, weder b noch c noch d.

Aber wenn ich schreibe

myFunction arg 
    | arg == 1 = a 
    | arg == 2 = a*b 
    | arg == 3 = b+c 
    | otherwise = (a+b)*c 
    where 
      (a,b,c,d) = anotherFunction arg 

Was wird Haskell Verhalten sein?

  • Wird es nur eine und 'propagieren' die lazyness zu anotherFunction bewerten?
  • Oder wird es das ganze Tupel (a, b, c, d) als Ergebnis von anotherFunction auswerten?
+1

Es wird das Tupel 'x = anotherFunction arg' ausgewertet, aber nicht alle _elements_ des Tupels. – Zeta

+2

Wenn Sie "call' myFunction 1 "sagen, nehme ich an, Sie meinen, wenn dieser Ausdruck ausgewertet wird. Wie Zeta sagt, wird es das Tupel (aber nicht die Elemente) auswerten und dann "a" aus diesem Tupel auswerten. – augustss

+0

@Zeta Ist es richtig zu sagen, dass die anderen Werte der Tupel "b", "c" und "d" in Thunkform sind. Oder gibt es dafür ein passenderes Wort? – Sibi

Antwort

29

In beiden Fällen wird nichts ausgewertet, außer der Wert wird angefordert. Eine Möglichkeit, den Wert anzufordern, besteht darin, die Funktion in ghci aufzurufen (die den Wert in ghci ausgibt und ihn daher anfordert). Unter der Annahme, dass Sie die Funktion ausführen, wird im zweiten Fall das Tupel auf weak head normal form (WHNF) ausgewertet und dann das erste Element in (a,b,c,d) ausgewertet, da nur dieser Wert angefordert wird. Die anderen Elemente b, c und d werden in der Thunk-Form sein. In der Tat, dass Sie selbst überprüfen können:

myFunction arg 
    | arg == 1 = a 
    | arg == 2 = a*b 
    | arg == 3 = b+c 
    | otherwise = (a+b)*c 
    where 
    (a,b,c,d) = anotherFunction arg 

anotherFunction x = (x, undefined, undefined, undefined) 

Demo in GHCI:

λ> myFunction 1 
1 
+5

@JeanJouX undefined sind sehr effektiv, um Nichtstriktheit in einer Sprache zu testen. – PyRulez

+0

@PyRulez Oh, warum? – thefourtheye

+1

@thefourtheye Iff etwas funktioniert, wenn eine der Eingaben nicht definiert ist, bedeutet es, dass das Programm nie versucht hat, es auszuwerten. – PyRulez

13

Nun, es ist nur daran interessiert a, so bedeutet das, dass es eine implizite Funktion:

thea :: (a,b,c,d) -> a 
thea (a,_,_,_) = a 

Mit anderen Worten Haskell nicht interessiert an den anderen Elementen des Tupels ist. Manchmal teilen die Elemente des Tupels jedoch eine gewisse Struktur. Say ist eine andere Funktion wie folgt definiert:

anotherFunction :: Int -> (Int,Int,Int,Int) 
anotherFunction x = (z,t,f,g) 
    where f = x*x 
      g = f+x 
      z = g-2 
      t = 3 

In diesem Fall -, um das erste Element zu bewerten - das dritte und vierte Element wird auch ausgewertet werden. Aber da du nichts mit ihnen machst, wird Haskell nicht besonders an ihrem Ergebnis interessiert sein.

7

Es sei denn, es notwendig ist, den Wert dieser Variablen zu erhalten, wird es nicht ausgewertet. Im Grunde ist Haskell so faul, es sei denn, man sagt es nicht.

Sie können dies bestätigen, wie diese

Prelude> :set +m 
Prelude> let anotherFunction = (100, 1 `div` 0) 
Prelude| 
Prelude> let myFunction arg 
Prelude|    | arg == 1 = a 
Prelude|    | otherwise = b 
Prelude|    where 
Prelude|      (a, b) = anotherFunction 
Prelude| 

Hier 1 `div` 0divide by zero Fehler ausgelöst wird.Wenn es alle Elemente ausgewertet wird, auch wenn Sie rufen myFunction mit 1, würden Sie diesen Fehler bekommen, aber

Prelude> myFunction 1 
100 

nur, wenn Sie es mit einem anderen Wert aufrufen, gibt es eine Notwendigkeit, den zweiten Wert zu bewerten des Tupels, und es wird mit divide by zero Fehler fehlschlagen.

Prelude> myFunction 2 
*** Exception: divide by zero 
11

Wie bereits erwähnt, wird nur a ausgewertet.

Behalten Sie jedoch im Hinterkopf, dass zur Ausnutzung der Faulheit es entscheidend ist, dass anotherFunction ein Tupel zurückgibt, bevor seine Komponenten bewertet werden. Betrachten wir zum Beispiel

anotherFunction n = if p > 1000 then (n, p) else (n, 0) 
    where p = product [1..n] 

Die oben wird immer product [1..n] bewerten, auch wenn der Anrufer verlangt nur das erste Paar Komponente (die n). Dies liegt daran, dass die if ausgewertet werden muss, bevor das Paar zurückgegeben werden kann, und dies erzwingt . Im Gegensatz dazu

anotherFunction n = (n, if p > 1000 then p else 0) 
    where p = product [1..n] 

wird sofort das Paar zurück. Wenn nur die erste Komponente ausgewertet wird, wird überhaupt nicht berechnet.