Sie können das Problem beheben, to10plus
indem Sie eine unwiderlegbare Übereinstimmung (dh ein ~
Präfix) in Ihrer Definition von merge
:
merge (a, b) ~(as, bs) = (a:as, b+bs)
Der Grund für den Unterschied im Verhalten zwischen to10
und to10plus
ist, dass to10
das erste Element der Liste zurückgegeben werden kann, ohne dass eine Bewertung erforderlich ist, to10 xs
und dies ohne Inspektion xs
.
Im Gegensatz dazu muss, bevor es etwas zurückgeben kann, to10plus
erfolgreich merge
mit den Argumenten (x, 1)
und aufgerufen werden to10plus xs
. Damit dieser Aufruf erfolgreich ist, to10plus xs
muss er weit genug bewertet werden, um sicherzustellen, dass er mit dem (as, bs)
in der Definition von verwendeten Muster übereinstimmt. Für diese merge
Bewertung müssen jedoch Elemente überprüft werden xs
, die noch nicht verfügbar sind.
Sie hätten das Problem auch vermeiden können, indem Sie es to10plus
etwas anders definiert hätten:
to10plus (x:xs) | x < 10 = (x:as,1+bs)
| otherwise = ([], 0)
where (as,bs) = to10plus xs
Hier to10plus
bieten kann das erste Element x
des ersten Teils des Tupels ohne zu bewerten zu versuchen as
, und so ohne Mustererkennung versucht , to10plus xs
mit (as,bs)
der where
Klausel. Eine let
Klausel hätte dasselbe getan:
to10plus (x:xs) | x < 10 = let (as,bs) = to10plus xs in (x:as,1+bs)
| otherwise = ([], 0)
Wie @luqui hervorhebt, ist dies ein Unterschied im Timing für Musterübereinstimmungen von let
und where
Anweisungen:
let (a,b) = expr in body
-- OR --
body where (a,b) = expr
versus case
Anweisungen / Funktionsdefinitionen:
case expr of (a,b) -> body
-- OR --
f (a,b) = body -- AND THEN EVALUATING: -- f expr
Die let
und where
-Anweisungen stimmen träge mit Mustern überein, was bedeutet, dass sie expr
erst dann mit dem Muster übereinstimmen, (a,b)
wenn sie in der a
oder b
ausgewertet werden body
. Im Gegensatz dazu wird für die case
Aussage sofort expr
abgestimmt (a,b)
, bevor die body
überhaupt geprüft wird. Und die obige Definition für gegeben f
, das Argument f
wird angepasst werden (a,b)
, sobald der Ausdruck , f expr
ohne zu warten , bis bewertet wird a
oder b
in der Funktion benötigt body
. Hier einige Arbeitsbeispiele zur Veranschaulichung:
ex1 = let (a,b) = undefined in print "okay"
ex2 = print "also okay" where (a,b) = undefined
ex3 = case undefined of (a,b) -> print "not okay"
ex4 = f undefined
f (a,b) = print "also not okay"
main = do
ex1 -- works
ex2 -- works
ex3 -- fails
ex4 -- fails
Durch Hinzufügen ~
wird das Verhalten für case
/ Funktionsdefinitionen geändert, sodass der Abgleich nur dann erfolgt, wenn a
oder wenn Folgendes b
erforderlich ist:
ex5 = case undefined of ~(a,b) -> print "works fine"
ex6 = g undefined
g ~(a,b) = print "also works fine"
ex7 = case undefined of ~(a,b) -> print $ "But trying " ++ show (a+b) ++ " would fail"
let
undwhere
sind immer faul, aber Muster für Argumente sind standardmäßig streng, es sei denn, sie werden faul verwendet~
.