为什么这些功能会有不同的评估

mor*_*ten 10 haskell lazy-evaluation

我正在尝试使用haskell,在尝试提高代码的可读性时,我突然改变了它的行为.我原以为这两种变体是等价的.

原版的:

f :: Eq c => c -> c -> [[c]] -> [[c]]
f d c acc  
  | c == d    = [] : acc
  | otherwise = ([c] ++ (head acc)) : tail acc

split :: Eq a => a -> [a] -> [[a]]
split delim = foldr (f delim) [[]]
Run Code Online (Sandbox Code Playgroud)

这是第二个:

f' :: Eq c => c -> c -> [[c]] -> [[c]]
f' d c (currentWord:wordsSoFar)  
  | c == d    = [] : currentWord : wordsSoFar
  | otherwise = (c : currentWord) : wordsSoFar

split' :: Eq a => a -> [a] -> [[a]]
split' delim = foldr (f' delim) [[]]
Run Code Online (Sandbox Code Playgroud)

以下是运行这两个的结果:

*Main> take 1 (split 5 [1..])
[[1,2,3,4]]
*Main> take 1 (split' 5 [1..])
*** Exception: stack overflow
Run Code Online (Sandbox Code Playgroud)

sep*_*p2k 8

你的第一个版本只需要评估acc,当你调用head,并tail在其上,所以没有评估时发生c == d.

第二个版本需要知道acc是否为空,然后才能执行任何其他操作,因为如果模式不匹配,则其他代码都不能执行.这意味着acc即使是必须进行评估c == d.这导致无限循环.

你可以使用这样的无可辩驳的模式使第二个版本工作:

f' d c ~(currentWord:wordsSoFar) =
Run Code Online (Sandbox Code Playgroud)

通过使模式无可辩驳,您说您知道模式将匹配,因此不需要检查.如果acc是空的,这会导致当(如果)你曾经发生的错误currentWordwordsSoFar代替非穷尽模式错误发生的时候了(且不论是否currentWordwordsSoFar实际使用).