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