Разбиение строки на несколько частей с разделением символа в Haskell - PullRequest
3 голосов
/ 19 июня 2019

У меня есть следующая домашняя работа:

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

Examples:

split '#' "foo##goo" = ["foo","","goo"]    
split '#' "#" = ["",""]

Я написал следующую функцию:

split :: Char -> String -> [String]
split c "" = [""]
split a "a" = ["",""]
split c st =  takeWhile (/=c) st : split c tail((dropWhile (/=c) st))

Она не компилируется, и я не понимаю, почему.TakeWhile добавляет все символы, которые не являются c, к результату, затем отбрасывает хвост, который c уже был найден, и мы рекурсивно применяем split к остальной части строки, полученной с помощью dropWhile.Команда: должна составить список «списков», поскольку строки - это списки символов в Haskell.Где разрыв в моем мышлении?

Обновление:

Я обновил свою программу до следующего:

my_tail :: [a]->[a]
my_tail [] = []
my_tail xs = tail xs

split :: Char -> String -> [String]
split c "" = [""]
split a "a" = ["",""]
split c st =  takeWhile (/=c) st ++ split c (my_tail(dropWhile (/=c) st))

Я все еще получаю сообщение об ошибке, следующее: enter image description here

Почему ожидается тип [String], а затем [Char]?

1 Ответ

4 голосов
/ 19 июня 2019

Причина, по которой это не компилируется, заключается в том, что Haskell видит ваше последнее предложение как:

split c st = takeWhile (/=c) st : split c tail ((dropWhile (/=c) st))

Таким образом, он думает, что вы применили три параметра к split: c, tail и ((dropWhile (/=c) st)).Здесь следует использовать скобки, например:

split c st = takeWhile (/=c) st : split c (tail (dropWhile (/=c) st))

Но это не решит проблему полностью.Например, если мы попытаемся запустить ваш тестовый сценарий, мы увидим:

Prelude> split '#' "foo##goo"
["foo","","goo"*** Exception: Prelude.tail: empty list

tail :: [a] -> [a] - это «неполная» функция.Для пустого списка tail будет ошибка.Действительно:

Prelude> tail []
*** Exception: Prelude.tail: empty list

В конечном итоге в списке не будет символов, а затем tail вызовет ошибку.Мы можем использовать span :: (a -> Bool) -> [a] -> ([a], [a]) здесь и использовать сопоставление с образцом, чтобы определить, есть ли еще какой-то элемент, который необходимо обработать, например:

split :: Eq a => a -> [a] -> [[a]]
split _ [] = [[]]
split c txt = pf : rst
    where rst | (_:sf1) <- sf = split c sf1
              | otherwise = []
          (pf,sf) = span (c /=) txt

Здесь span (c /=) txt, таким образом, разделит непустой список txt на две части pf ( prefix ) - самый длинный префикс элементов, которые не равны c.sf (суффикс) - остальные элементы.

Независимо от того, пусто sf или нет, мы передаем префикс pf.Затем мы проверяем суффикс.Мы знаем, что либо sf пусто (мы достигли конца списка), либо что первый элемент sf равен c.Таким образом, мы используем pattern guard , чтобы проверить, соответствует ли это шаблону (_:sf1).Это происходит, если sf не пусто.В этом случае мы связываем sf1 с хвостом sf, и мы повторяем на хвосте.Если sf1 пусто, мы можем остановиться и, таким образом, вернуть [].

Например:

Prelude> split '#' "foo##goo"
["foo","","goo"]
Prelude> split '#' "#"
["",""]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...