Неверный вывод с функцией Haskell, которая использует списки и zip - PullRequest
0 голосов
/ 07 апреля 2019

Мой код должен принимать строку, например, ccccchhrrre и возвращать список пар [('c', 5) , ('h', 2) , ('r', 3), ('e', 1)].Первая часть пары представляет собой букву, а вторая часть - количество повторений до следующей строки.

В настоящее время, однако, мой код возвращает [('c',11)] - это первый символ и длина общего количества символов в строке:

> fun4 "ccccchhrrre"
[('c',11)]

Моя проблема связана с fun4,При выполнении func3

> fun3 "ccccchhrrre"
["ccccc","hh","rrr","e"]
fun3 :: String -> [String]
fun3 "" = [] 
fun3 xs = fun2 xs : fun3 (drop(length(fun2 xs))xs)

func4 :: String -> [(Char, Int)]
func4 fun3 = zip (fun3)[length fun3]

я ценю любую помощь, которая включает подробные объяснения.

Ответы [ 2 ]

4 голосов
/ 07 апреля 2019

Есть пара вопросов.Во-первых, когда вы пишете:

func4 :: String -> [(Char, Int)]
func4 fun3 = zip (fun3)[length fun3]

* fun3 в этом определении не имеет абсолютного ничего , связанного с ранее определенной вами функцией fun3.Если вы включили предупреждения компилятора (например, используя флаг -Wall), он предупредит вас, что привязка для fun3 в этом определении func4 "затеняет" существующую привязку.Это означает, что он обрабатывается так же, как если бы вы использовали полное отдельное имя:

func4 :: String -> [(Char, Int)]
func4 bob = zip (bob) [length bob]

Итак, когда вы оцениваете:

func4 "ccccchhrrre"

функция fun3 неиспользуется на всех.Вместо этого он расширяется по определению func4 до:

func4 "ccccchhrrre"
= zip "ccccchhrrre" [length "ccccchhrrre"]
= zip "ccccchhrrre" [11]

, и поскольку строки в Haskell представляют собой списки символов, это то же самое, что:

= zip ['c','c',...] [11]

Когда вы архивируетедва списка, и один короче другого, застежка-молния заканчивается, когда список заканчивается, поэтому это дает:

= [('c', 11)]

и игнорирует второй и последующие элементы списка символов.

Вместо этого вы, вероятно, хотели написать func4, чтобы получить строковый аргумент и передать его fun3:

func4 :: String -> [(Char, Int)]
func4 xs = zip (fun3 xs) [length (fun3 xs)]

К сожалению, это не будет проверкой типа.Проблема в том, что это эквивалентно:

func4 "ccccchhrrre"
= zip ["ccccc","hh","rrr","e"] [length ["ccccc","hh","rrr","e"]]
= zip ["ccccc","hh","rrr","e"] [4]
= [("ccccc", 4)]

с типом [(String, Char)] вместо ожидаемого [(Char, Char)].

Если вы узнали о map,Вам может быть полезно знать, что:

> map head ["ccccc","hh","rrr","e"]
"chre"
>

и "chre" эквивалентны списку ['c','h','r','e'].Если бы вы могли каким-то образом получить список длин [5,2,3,1] так же, как вы получили список головок, эти списки были бы очень удобными.

Если вы не хотите использовать map, выможет оказаться полезным написать func4, который обрабатывает вывод из fun3, например, так:

> func4 ["ccccc","hh","rrr","e"]
[('c',5),('h',2),('r',3),('e',1)]
>

Он будет иметь немного другую подпись, но структуру, аналогичнуюваш fun3:

func4 :: [String] -> [(Char, Int)]
func4 [] = []
func4 (str : strs) = ??? : func4 strs

, а затем ваша последняя функция может соединить fun3 и func4 вместе:

rle :: String -> [(Char, Int)]
rle str = func4 (fun3 str)
0 голосов
/ 14 апреля 2019

Другой ответ дает хорошее объяснение проблем в вашем коде. Вот немного другое решение, которое мне легче понять.

fun3 можно заменить существующей функцией, Data.List.group. group берет список и создает список списков, которые содержат смежные элементы, равные:

> Data.List.group "ccccchhrrre"
["ccccc","hh","rrr","e"]

Работа, которую должен выполнять fun4, также может быть упрощена использованием map. Анонимная функция \x -> (head x, length x) принимает один аргумент x и создает кортеж, первым элементом которого является глава x, а вторым элементом - длина x. Отображение этой функции на вывод group должно дать нам желаемый результат:

> map (\x -> (head x, length x)) ["ccccc","hh","rrr","e"]
[('c',5),('h',2),('r',3),('e',1)]

Теперь мы можем объединить group и наш map в одну функцию и дать ей более подходящее имя:

-- Count adjacent repeated characters
countRep :: String -> [(Char, Int)]
countRep str = map (\x -> (head x, length x)) (List.group str)

Просто для удовольствия

Функцию, которую мы написали, можно использовать в любом списке, элементы которого можно сравнить на равенство! Вот он, в бессмысленном стиле:

countRep :: Eq a => [a] -> [(a, Int)]
countRep = map (liftA2 (,) head length) . List.group
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...