Haskell странное (для меня) поведение
Я работаю над 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.
Я успешно решил эту проблему с помощью функции foldr.
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>
Я чувствую, что это что-то вроде лени в Хаскеле... Это мое лучшее предположение. Кто-нибудь может объяснить, почему я получаю этот странный результат во время выполнения и корректный результат во время выполнения с точкой останова?
1 ответ
Проблема исходит из выражения ниже:
x == last acc = acc ++ f acc xs
Не нужно добавлять acc
строка в начале результата, поэтому исправление должно быть:
x == last acc = f acc xs
Обратите внимание, что acc
содержит правильный результат, который вы хотите, т.е. строку без последовательных дубликатов, следовательно, вы можете увидеть правильный результат acc :: [Char] = "abcade"
в точке останова, когда список ввода []
, Но когда он возвращается, он объединяет предыдущий результат как acc ++ "abcade"
из которого "Абкада" в конце "aaaabcabcaabcadeabcadeabcadeabcade"