Концепция сопоставления шаблонов Haskell за разделителем текста

Я хочу знать концепцию сопоставления с образцом, стоящую за этим фрагментом кода:

 split :: String -> Char -> [String]
 split [] delim = [""]
 split (c:cs) delim
     | c == delim = "" : rest
     | otherwise = (c : head rest) : tail rest
       where
         rest = split cs delim

Я знаю, что head возвращает 1-й элемент списка, а tail возвращает остальные. Но я все еще не могу понять функциональность этого. Это берет строку и разбивает ее на список строк из заданного символа.


person Gihan    schedule 06.02.2012    source источник


Ответы (1)


Может быть, это понятнее в следующей форме:

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
person Daniel Fischer    schedule 06.02.2012
comment
Таким образом, добавление пустой строки к from разбивает строку. Я имею в виду, что строки разделены на список строк. .? Если найден символ-разделитель, что он вернет...? как в примере будет сказано, что строка с именем abc cde должна быть отделена от пробела.. тогда, когда у нее есть ' ', она вернет, cde или cde.. и принимает ли head список строк..? - person Gihan; 06.02.2012
comment
@Gihan split "abc cde" ' ' создаст ["abc","cde"]. Я не уверен, что понимаю ваш следующий вопрос, но если я понимаю: head принимает список любого типа, в split он действительно вызывается для списка String. - person Daniel Fischer; 06.02.2012
comment
Да, это правда. Что касается следующего вопроса, то он такой. Скажет, что строка, которую я хочу разбить, равна "abc cde", так как у нее 7 символов, у нее 7 стадий рекурсии. начнется с финального этапа. Он вернет "E" . 6-й повтор возвратит E:F:Null как в "EF". 5-й вернет D:E:F как в "DEF". затем идет место на 4-м уровне. Тогда это будет "":"DEF" правильно. Так вот тут возникает вопрос. Будет ли он возвращен оттуда как ("","DEF") или "DEF"...? - person Gihan; 06.02.2012
comment
Опять же, если это ("","DEF"), на 3-м уровне он сам будет C:"":"DEF" возвращать "C","","DEF" правильно..? - person Gihan; 06.02.2012
comment
Я расширил свой ответ, возможно, это достаточно ясно (если нет, спросите еще раз). - person Daniel Fischer; 06.02.2012
comment
Я понял, братан. я думал назад. Спасибо за помощь. Действительно ценю это. Только еще один момент, почему мы используем пустую строку. В чем особенность..? - person Gihan; 06.02.2012
comment
Не уверен, что понимаю. Каждый (конечный и полностью определенный [корешок]) список заканчивается пустым списком []. Всякий раз, когда достигается разделитель (или конец ввода), вставляется пустой список (из Chars, пустой String), чтобы отметить конец одного фрагмента. - person Daniel Fischer; 06.02.2012