Почему эти функции по-разному оцениваются

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