Разделить строку на список строк в Clean
Из-за ограниченного количества ресурсов, мне нужно предложить вопрос здесь. Я боролся с функциональным программированием, бесконечные учебники по Хаскеллу не очень мне помогают. Итак, что я хочу достичь, в чистом языке, это разделить строку как " car cow cat "
к списку строк ["car","cow","cat"]
, Можете ли вы дать мне подробный ответ (не обязательно полный код) о том, как перебирать эту строку, и особенно ту часть, когда вновь созданные строки добавляются в список?
2 ответа
Я собираюсь предложить простое решение. Существуют бесконечно лучшие способы сделать это в Haskell, но это самый простой, который я могу придумать для кого-то новичка в функциональном программировании, без использования какой-либо специальной функции Haskell, такой как takeWhile, или даже каких-либо сгибов и карт...
Вы хотите симулировать итерации по списку, поэтому я предлагаю следующее:
Определите функцию, которая будет принимать строку и разделенный символ. Эта функция вернет список строк -
spliton :: String -> Char -> [String]
Чтобы перемещаться по списку, мы хотим сожрать символы, пока не нажмем один из наших символов разделения. Мы также хотим сохранить слово, которое мы сохранили до сих пор, и весь список слов. Для этого мы определим подфункцию, которая будет сохранять состояния
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
Итак, подведем итог нашего кода:
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
Примечание: Это, однако, не избавит от пустой строки - то есть, если у вас много лишних пробелов - вы добавите их в список. Я оставлю вас думать, как справиться с этим делом - надеюсь, это поможет вам начать.
Давайте разделим это на несколько подзадач:
- Составьте список символов из строки, чтобы мы могли легко применять сопоставление с образцом.
- Очистите начальную часть списка (как можно дольше, используя только пробелы или только не-пробелы), и сохраняйте ее только тогда, когда она не является пробелом.
- Повторите второй шаг, пока список не пуст.
Первое, что можно сделать с помощью 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)
соответственно.