Haskell的行为(对我而言)很奇怪

时间:2018-12-17 07:51:29

标签: haskell computation

我正在研究99个Haskell问题(https://wiki.haskell.org/99_questions/1_to_10) ,我对问题8有一个疑问。

8 Problem 8
(**) Eliminate consecutive duplicates of list elements.

If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed.

我已成功使用文件夹功能解决了此问题。

compress :: Eq e => [e] -> [e]
compress =  let f v [] = [v]
                f v acc 
                        | head acc == v = acc
                        | otherwise = v:acc
            in foldr f []

但是当我尝试通过递归来解决同样的问题时,

compress' :: Eq e => [e] -> [e]
compress' = let f acc [] = acc
                f [] (x:xs) = f [x] xs
                f acc (x:xs) | x == last acc = acc ++ f acc xs
                             | otherwise = f (acc ++ [x]) xs 
            in f []

我看到了非常奇怪的行为。我看到此功能的结果:

compress' "aaaabccaadeeee"
"aaaabcabcaabcadeabcadeabcadeabcade"

但是如果我在第

行添加断点
compress' = let f acc [] = acc

它给我正确的结果:

ghci> compress' "aaaabccaadeeee"
"aaaabcabcaabcadeabcadeabcadeabcade"
ghci> :break 304
Breakpoint 7 activated at haskell-tut.hs:304:28-30
ghci> compress' "aaaabccaadeeee"
"aaaabcabcaabcadeabcadeabcadeStopped in Main.compress'.f, haskell-tut.hs:304:28-30
_result :: [Char] = _
acc :: [Char] = "abcade"
[haskell-tut.hs:304:28-30] ghci> :con
abcade"
ghci>

我觉得这与Haskell懒惰有关。...这是我最好的假设。 谁能解释为什么我在执行过程中会得到这个奇怪的结果,而在执行过程中使用断点才能得到正确的结果呢?

1 个答案:

答案 0 :(得分:5)

问题来自下面的表达式:

x == last acc = acc ++ f acc xs

不需要在结果的开头附加acc字符串,因此更正应为:

x == last acc = f acc xs

请注意,acc包含所需的正确结果,即没有连续重复的字符串,因此当输入列表为acc :: [Char] = "abcade"时,您可以在断点处看到正确的结果[] 。但是,当返回时,它会将先前的结果合并为acc ++ "abcade""aaaabcabcaabcadeabcadeabcadeabcade"末尾的“拱廊”来自该结果