Почему эти функции оцениваются по-разному
я экспериментировал с 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 == []; Я полагаю, этого никогда не происходит.)