dpa*_*tru 8 haskell lazy-sequences
为什么我<<loop>>
在这里得到一个无限循环 ( ) 运行时错误?
文件反馈.hs:
plus1 :: [Int]->[Int] -- add 1 to input stream
plus1 [] = []
plus1 (x:xs) = (x+1): plus1 xs
to10 :: [Int] -> [Int] -- stop the input stream when it gets to 10
to10 (x:xs) | x < 10 = x : to10 xs
| otherwise = []
to10plus :: [Int] -> ([Int], Int) -- like to10 but also return the count
to10plus (x:xs) | x < 10 = (x, 1) `merge` (to10plus xs)
| otherwise = ([], 0)
where merge (a, b) (as, bs) = (a:as, b+bs)
main = do
let out = plus1 $ 1: to10 out
putStrLn $ show out -- gives [2,3,4,5,6,7,8,9,10]
let out = plus1 $ 1: out2
out2 = to10 out
putStrLn $ show out -- same as above
let out = plus1 $ 1: out2
(out2, count) = to10plus out
putStrLn $ show (out, count) -- expect ([2,3,4,5,6,7,8,9,10], 8)
-- but get runtime error: <<loop>>
Run Code Online (Sandbox Code Playgroud)
$ ghc feedback.hs
[1 of 1] Compiling Main ( feedback.hs, feedback.o )
Linking feedback ...
$ ./feedback
[2,3,4,5,6,7,8,9,10]
[2,3,4,5,6,7,8,9,10]
feedback: <<loop>>
Run Code Online (Sandbox Code Playgroud)
您可以to10plus
通过~
在您的定义中使用无可辩驳的匹配(即前缀)来修复merge
:
merge (a, b) ~(as, bs) = (a:as, b+bs)
Run Code Online (Sandbox Code Playgroud)
to10
和之间行为差异的原因to10plus
是to10
可以返回列表的第一个元素而无需评估to10 xs
,因此无需检查xs
。
相反,在它可以返回任何东西之前,to10plus
必须成功调用merge
参数(x, 1)
和to10plus xs
。要使此调用成功,to10plus xs
必须进行足够远的评估以确保它与(as, bs)
的定义中使用的模式相匹配merge
,但该评估需要检查 的元素xs
,而这些元素尚不可用。
你也可以通过to10plus
稍微不同的定义来避免这个问题:
to10plus (x:xs) | x < 10 = (x:as,1+bs)
| otherwise = ([], 0)
where (as,bs) = to10plus xs
Run Code Online (Sandbox Code Playgroud)
在这里,to10plus
可以提供第一个元素x
的元组的第一部分,但不尝试评估as
,所以没有尝试模式匹配to10plus xs
与(as,bs)
中where
子句。一个let
子句会做同样的事情:
to10plus (x:xs) | x < 10 = let (as,bs) = to10plus xs in (x:as,1+bs)
| otherwise = ([], 0)
Run Code Online (Sandbox Code Playgroud)
正如@luqui 指出的那样,这与let
和where
语句进行模式匹配的时间不同:
let (a,b) = expr in body
-- OR --
body where (a,b) = expr
Run Code Online (Sandbox Code Playgroud)
与case
语句/函数定义:
case expr of (a,b) -> body
-- OR --
f (a,b) = body -- AND THEN EVALUATING: -- f expr
Run Code Online (Sandbox Code Playgroud)
的let
和where
语句懒惰地匹配的模式,这意味着expr
不匹配于图案(a,b)
直至a
或b
在被评估body
。相比之下,对于case
语句,在甚至检查之前立即expr
匹配。并且给定上述 for 的定义,参数 to将在表达式被评估后立即匹配,而无需等到或函数中需要。以下是一些工作示例来说明:(a,b)
body
f
f
(a,b)
f expr
a
b
body
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
Run Code Online (Sandbox Code Playgroud)
添加会~
更改case
/ 函数定义的行为,因此匹配仅在需要a
或b
需要时发生:
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"
Run Code Online (Sandbox Code Playgroud)