Почему эти функции оцениваются по-разному
я экспериментировал с 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) [[]]
второй вопрос:
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) [[]]
вот результаты запуска двух:
*Main> take 1 (split 5 [1..])
[[1,2,3,4]]
*Main> take 1 (split' 5 [1..])
*** Exception: stack overflow
2 ответов
ваша первая версия должна только оценить acc
когда вы называете head
и tail
на нем, поэтому никакой оценки не происходит, когда c == d
.
вторая версия должна знать, является ли acc пустым или нет, прежде чем он делает что-либо еще, поскольку ни один из другого кода не должен выполняться, если шаблон не соответствует. Это значит, что acc
должен быть оценен, даже если c == d
. Это приводит к бесконечному циклу.
вы можете сделать вторую версию работы с помощью неопровержимая закономерность такая:
f' d c ~(currentWord:wordsSoFar) =
делая шаблон неопровержимым, вы говорите, что знаете, что шаблон будет соответствовать, поэтому проверка не требуется. Если acc
были пусты, это приведет к ошибке, когда (и если) вы использовали currentWord
и wordsSoFar
вместо неисчерпывающей ошибки шаблона происходит сразу (и независимо от того,currentWord
и wordsSoFar
на самом деле используется).
Я думаю этой должно быть эквивалентно:
f d c acc | c == d = [] : acc
f d c (currentWord:wordsSoFar) = (c : currentWord) : wordsSoFar
обратите внимание, что если c == d
затем мы не пытаемся определить, является ли acc
пустой или нет. (Все версии вашего кода фактически завершатся неудачей, если c == d
и acc == []
; Я полагаю, этого никогда не происходит.)