Возможно, это будет понятнее в следующей форме:
split [] delim = [""] -- a list containing only an empty String
split (c:cs) delim = let (firstWord:moreWords) = split cs delim
in if c == delim
then "" : firstWord : moreWords
else (c:firstWord) : moreWords
Функция обходит входную строку, сравнивая каждый символ с разделителем. Если текущий символ не является символом-разделителем, он добавляется в начало первого слова (которое может быть пустым) в результате разбиения оставшейся части строки, если это символ-разделитель, он добавляет пустую строку вперед результата разбиения остатка.
Например, оценка split "abc cde" ' '
происходит как
split "abc cde" ' '
~> 'a' == ' ' ? No, next guard
~> ('a' : something) : somethingElse
, где something
и somethingElse
будут определены позже, разделив остаток "bc cde". After looking at the first character, it's been determined that whatever the final result is, its first entry starts with
'a'`. Продолжая определять остальное,
split "bc cde" ' '
~> ('b' : something1) : somethingElse1
where (something1 : somethingElse1) = split "c cde" ' '
Итак, теперь первые два символа первой записи результата известны. Затем из следующего шага определяется, что something1
начинается с 'c'
. Затем, наконец, мы достигаем разделитель, то есть тот случай, когда первый элемент результата определяется без ссылки на более поздние рекурсивные вызовы, и в рекурсии остается только остаток результата.
Другой способ сформулировать алгоритм (спасибо @ dave4420 за предложение)
split input delim = foldr combine [""] input
where
combine c rest@(~(wd : wds))
| c == delim = "" : rest
| otherwise = (c : wd) : wds