Bạn có thể khắc phục to10plus
bằng cách sử dụng kết quả khớp không thể bác bỏ (nghĩa là ~
tiền tố) trong định nghĩa của bạn về merge
:
merge (a, b) ~(as, bs) = (a:as, b+bs)
Lý do cho sự khác biệt trong hành vi giữa to10
và to10plus
là to10
có thể trả về yếu tố đầu tiên của danh sách mà không phải đánh giá to10 xs
và do đó mà không cần kiểm tra xs
.
Ngược lại, trước khi nó có thể trả về bất cứ thứ gì, to10plus
phải gọi thành công merge
với các đối số (x, 1)
và to10plus xs
. Để cuộc gọi này thành công, to10plus xs
phải được đánh giá đủ xa để đảm bảo nó phù hợp với mẫu (as, bs)
được sử dụng trong định nghĩa merge
, nhưng đánh giá đó yêu cầu kiểm tra các yếu tố của xs
, chưa có sẵn.
Bạn cũng có thể tránh được vấn đề bằng cách định nghĩa to10plus
một chút khác biệt:
to10plus (x:xs) | x < 10 = (x:as,1+bs)
| otherwise = ([], 0)
where (as,bs) = to10plus xs
Ở đây, to10plus
có thể cung cấp các yếu tố đầu tiên x
của phần đầu tiên của tuple mà không cần cố gắng để đánh giá as
, và như vậy mà không cố gắng để phù hợp với mô hình to10plus xs
với (as,bs)
trong where
khoản. Một let
mệnh đề sẽ làm điều tương tự:
to10plus (x:xs) | x < 10 = let (as,bs) = to10plus xs in (x:as,1+bs)
| otherwise = ([], 0)
Như @luqui chỉ ra, đây là một sự khác biệt về thời gian cho các kết quả khớp mẫu được thực hiện bởi let
và các where
câu lệnh:
let (a,b) = expr in body
-- OR --
body where (a,b) = expr
so với case
các định nghĩa / định nghĩa hàm:
case expr of (a,b) -> body
-- OR --
f (a,b) = body -- AND THEN EVALUATING: -- f expr
Các câu lệnh let
và các where
câu lệnh khớp với các mẫu một cách lười biếng, có nghĩa expr
là không khớp với mẫu (a,b)
cho đến khi a
hoặc b
được đánh giá trong body
. Ngược lại, đối với case
tuyên bố, expr
được khớp với (a,b)
ngay lập tức, trước khi body
thậm chí được kiểm tra. Và đưa ra định nghĩa trên cho f
, đối số f
sẽ được khớp với (a,b)
ngay khi biểu thức f expr
được ước tính mà không cần đợi cho đến khi a
hoặc b
cần thiết trong hàm body
. Dưới đây là một số ví dụ hoạt động để minh họa:
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
Việc thêm ~
thay đổi hành vi cho case
/ định nghĩa hàm để việc khớp chỉ diễn ra khi a
hoặc b
cần thiết:
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
vàwhere
luôn lười biếng, nhưng mẫu cho các đối số mặc định là nghiêm ngặt trừ khi lười sử dụng~
.