Разбор строки в список бинарных кортежей - PullRequest
0 голосов
/ 08 ноября 2018

Я пытаюсь разобрать строку "A1B2C3D4" до [('A',1),('B',2),('C',3)] в Haskell.

Я пытаюсь использовать map вот так map (\[a, b] -> (a :: Char, b :: Int)) x, где x - строка.

Это подпись функции, которой я должен следовать :: String -> [(Char, Int)].

К сожалению, я получаю несоответствия типов, может кто-нибудь дать подсказку, как решить эту проблему?

Я в правильном направлении?

Ответы [ 4 ]

0 голосов
/ 08 ноября 2018

Как уже отмечали другие, вы должны понимать, что map применяет данную функцию к каждому члену списка. Как только вы поймете это, вы поймете, что вы не сможете получить желаемую конверсию, применив функцию к существующему списку.

Это приводит к осознанию того, что как только у вас есть список «A1», «B2», ..., вы можете взять их и преобразовать, используя функцию карты.

Я дал код для функции ниже. Функция split' небезопасна, так как она может взорваться во многих случаях (ожидается строка, которая может быть идеально разделена на 2 символа). Я также использую функцию digitToInt, для которой вам нужно import Data.Char. Вы сказали, что не хотите импортировать, в этом случае вы можете написать свою собственную функцию digitToInt, посмотреть код библиотеки, это довольно просто.

import Data.Char

split' :: String -> [String]
split' [] = []
split' (x:y:xs) = (x:[y]) : split' xs

convert :: String -> [(Char, Int)]
convert input = map (\s -> (s!!0 ,  digitToInt(s!!1) )) $ split' input
0 голосов
/ 08 ноября 2018

Я придумал это, но на самом деле это небезопасно, поскольку он не обрабатывает неправильную строку, такую ​​как "AA11B2C3"!

splitingN :: Int -> [a] -> [[a]]
splitingN _ [] = []
splitingN n l
  | n > 0 = take n l : splitingN n (drop n l)
  | otherwise = error "uhhhhhh"

tuplify :: String -> (Char, Int)
tuplify a = (head a, read $ tail a)

stringy :: String -> [(Char, Int)]
stringy s = tuplify <$> splitingN 2 s

> stringy "A1B2C3D4" == [('A',1),('B',2),('C',3),('D',4)]

Гораздо более приятный способ, но все же не полностью безопасный:

stringy :: [a] -> [(a, a)]
stringy [] = []
stringy (a : b : rest) = (a, b) : splitting rest
stringy [a] = error "uhhhhh"

Должен действительно проверить, действительно ли a и b из (a : b : rest) Char & Int. Также здесь используется рекурсия, и вы упомянули об использовании map, поэтому может быть недостаточно, и это довольно полиморфно в своих типах.

0 голосов
/ 08 ноября 2018

Ну, map на самом деле предназначен для применения одной функции к каждому элементу чего-то, один за другим. Для разбиения строки, как вы хотите, требуется контекст (зная следующую букву), поэтому map не лучший выбор здесь.

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

isDigit :: Char -> Bool
isDigit c = elem c ['0'..'9']

split :: String -> [(Char, Int)]
split str = let chars  = filter (not . isDigit) str
                nums   = filter isDigit str
                zipped = zip chars nums in
              map (\(a, b) -> (a, read [b])) zipped
0 голосов
/ 08 ноября 2018

Есть несколько проблем.

  1. Шаблон [a, b] в map (\[a, b] -> ...) x соответствует только спискам двух элементов, поэтому компилятор делает вывод, что функция \[a, b] -> ... имеет тип [r] -> s для некоторых r и s.

    Компилятор знает, что map имеет тип (u -> v) -> [u] -> [v], поэтому он объединяет u с [r] и v с s для вывода типа [[r]] -> [s] для map (\[a, b] -> ...).

    Это означает, что x должен иметь тип [[r]], то есть это должен быть список списков. Но вы хотите, чтобы x было String, что является синонимом [Char]. Компилятор не может объединить [[r]] и [Char], поэтому он возражает.

  2. Вы пытаетесь "привести" a к Char и b к Int, как вы это делали в C, но вы не можете сделать это в Haskell. Если вы хотите преобразовать Char как '1' в Int 1, вам нужен другой подход, например read, который вы можете использовать для преобразования из String в Int.

Вот несколько советов. Не используйте map. Попробуйте вместо этого написать рекурсивное решение.

Начните с рассмотрения нескольких случаев:

  • что возвращает myParser ""? 1056 *
  • что возвращает myParser "a1"
  • что возвращает myParser [a,b]? 1062 *
  • что возвращает myParser (a:b:cs)
...