Разделить строку на список строк в Clean

Из-за ограниченного количества ресурсов, мне нужно предложить вопрос здесь. Я боролся с функциональным программированием, бесконечные учебники по Хаскеллу не очень мне помогают. Итак, что я хочу достичь, в чистом языке, это разделить строку как " car cow cat " к списку строк ["car","cow","cat"], Можете ли вы дать мне подробный ответ (не обязательно полный код) о том, как перебирать эту строку, и особенно ту часть, когда вновь созданные строки добавляются в список?

2 ответа

Решение

Я собираюсь предложить простое решение. Существуют бесконечно лучшие способы сделать это в Haskell, но это самый простой, который я могу придумать для кого-то новичка в функциональном программировании, без использования какой-либо специальной функции Haskell, такой как takeWhile, или даже каких-либо сгибов и карт...

Вы хотите симулировать итерации по списку, поэтому я предлагаю следующее:

  1. Определите функцию, которая будет принимать строку и разделенный символ. Эта функция вернет список строк - spliton :: String -> Char -> [String]

  2. Чтобы перемещаться по списку, мы хотим сожрать символы, пока не нажмем один из наших символов разделения. Мы также хотим сохранить слово, которое мы сохранили до сих пор, и весь список слов. Для этого мы определим подфункцию, которая будет сохранять состояния

    spliton' :: String -> Char -> String -> [String] -> [String]

    spliton' [] _ sofar res = res ++ [sofar]

    Я также включил простейшее предложение - пустую строку. Когда наша строка пуста, мы просто хотим вернуть то, что мы сохранили до сих пор.

  3. Теперь давайте перейдем к нашей действительной рекурсивной функции: если мы нажмем наш символ разделения, мы добавим в список строку, которую мы сохранили до сих пор, и перезапустим с пустой строкой текущего состояния. Если мы не нажмем символ разделения, мы добавим символ в строку текущего состояния

    spliton' (currchar:rest) splitby sofar res
         | currchar==splitby = spliton' rest splitby "" (res++[sofar])
         | otherwise = spliton' rest splitby (sofar++[currchar]) res
    

Итак, подведем итог нашего кода:

spliton :: String -> Char -> [String]
spliton source splitchar = spliton' source splitchar [] []

spliton' :: String -> Char -> String -> [String] -> [String]
spliton' [] _ sofar res = res ++ [sofar]
spliton' (currchar:rest) splitby sofar res
         | currchar==splitby = spliton' rest splitby "" (res++[sofar])
         | otherwise = spliton' rest splitby (sofar++[currchar]) res

Примечание: Это, однако, не избавит от пустой строки - то есть, если у вас много лишних пробелов - вы добавите их в список. Я оставлю вас думать, как справиться с этим делом - надеюсь, это поможет вам начать.

Давайте разделим это на несколько подзадач:

  1. Составьте список символов из строки, чтобы мы могли легко применять сопоставление с образцом.
  2. Очистите начальную часть списка (как можно дольше, используя только пробелы или только не-пробелы), и сохраняйте ее только тогда, когда она не является пробелом.
  3. Повторите второй шаг, пока список не пуст.

Первое, что можно сделать с помощью fromString, Для второго и третьего шага мы определим вспомогательную функцию:

scrape :: [Char] -> [String]
scrape [] = []
scrape cs=:[c:_]
| isSpace c = scrape (dropWhile isSpace cs)
| otherwise = [toString word:scrape rest]
where
    (word,rest) = span (not o isSpace) cs

Первая альтернатива - это базовый вариант, соответствующий пустому списку. Второй вариант соответствует всему списку cs с первым элементом c, Если первый символ является пробелом, мы рекурсивно (шаг 3) вызываем ту же функцию в том же списке без начальной части пробелов. Если первый символ не пробел, мы используем span :: (a -> Bool) [a] -> ([a], [a]) разделить список на начальную часть, то есть слово, а остальное. Мы храним слово, используя toString в виде строки, и рекурсивно вызвать scrape для остальной части списка.

Теперь нам нужна только оболочка, чтобы сделать это функцией с типом String -> [String]:

split :: String -> [String]
split s = scrape (fromString s)
where
    scrape :: [Char] -> [String]
    scrape [] = []
    scrape cs=:[c:_]
    | isSpace c = scrape (dropWhile isSpace cs)
    | otherwise = [toString word:scrape rest]
    where
        (word,rest) = span (not o isSpace) cs

Обратите внимание, что вы можете легко абстрагироваться от разделителя, передав символ d и замена isSpace c с c == d а также (not o isSpace) от ((<>) d), Кроме того, вы можете выбрать, чтобы не передать символ d но функция isDelim :: Char -> Bool, Вы тогда получаете isDelim c а также (not o isDelim)соответственно.

Другие вопросы по тегам