Как преобразовать строку в фактический термин в Haskell? - PullRequest
0 голосов
/ 12 июня 2018

Я определил следующий тип данных

data EventType = RUN_EVENT Integer | GIVE_ITEM Integer Integer ... deriving (Show) 

, и из какого-то теста я получаю данные, как показано ниже

["СОБЫТИЕ", "6001", "E "," RUN_EVENT "," 6010 "]

и когда я обрабатываю данные, если, например, после" E "я получаю" RUN_EVENT ", я хотел бы построить данныетипа EventType со значением RUN_EVENT 6010 .

Для этого я могу создать огромный список, который в основном говорит, что если я столкнусь с «RUN_EVENT», то создамзначение со значением RUN_EVENT , но это не будет эффективным, поэтому есть ли способ преобразовать "RUN_EVENT" в термин RUN_EVENT , чтобы я мог использовать его как

exec = (stringToTerm "RUN_EVENT") 6010

Ответы [ 4 ]

0 голосов
/ 12 июня 2018

Вот Hacky решение спорным с использованием Read

import Text.Read (readMaybe)

data EventType = RUN_EVENT Integer | GIVE_ITEM Integer Integer ... 
     deriving (Read, Show)

parseChunk :: [String] -> Maybe EventType
parseChunk = readMaybe . unwords

Это будет работать как

parseChunk ["RUN_EVENT","4"]     -->  Just (RUN_EVENT 4)
parseChunk ["GIVE_ITEM","1","2"] -->  Just (GIVE_ITEM 1 2)
parseChunk ["RUN_EVENT","4","5"] -->  Nothing  -- too many arguments

Причина это Hacky является то, что она опирается на манипуляции строки, чтобы преобразовать вашпоток данных в выражение Haskell (которое read анализирует).Если ваш синтаксический анализатор усложняется, подход может сломаться, или его хакерство может выйти из-под контроля.Представьте, что один из ваших конструкторов берет список, затем вы должны начать вставлять "[" и "," в поток перед тем, как read его.

Если вы хотите больше контроля, лучший способ этонаписать правильный парсер, возможно, используя Parsec или его аналог.Но вам придется либо перечислить все случаи вручную или сгенерировать случаи, используя generics .Моя собственная эстетика позволила бы решению read остаться в силе, но затем переключиться на формальный синтаксический анализатор, если казалось, что он становится беспорядочным.

0 голосов
/ 12 июня 2018

Непосредственной реализацией вашего вопроса может быть:

parseEventType :: [String] -> EventType
parseEventType ["EVENT", _, _, "RUN_EVENT", i] = RUN_EVENT (read i)
parseEventType ["EVENT", i, _, "GIVE_ITEM", j] = GIVE_ITEM (read i) (read j)
parseEventType x = error ("parseEventType: could not parse " ++ show x)

Случай GIVE_ITEM может быть неправильным, но он должен дать вам представление.

Однако, это имеет некоторые проблемы, в основномвокруг обработки ошибок: что вы делаете, если аргументы не являются целыми числами или если форма списка не соответствует ни одному из ваших типов?Было бы проще использовать Maybe:

parseEventType :: [String] -> Maybe EventType
parseEventType ["EVENT", _, _, "RUN_EVENT", i] = RUN_EVENT <$> readMay i
parseEventType ["EVENT", i, _, "GIVE_ITEM", j] = GIVE_ITEM <$> readMay i <*> readMay j
parseEventType x = Nothing

Это демонстрирует аппликативный стиль.

0 голосов
/ 12 июня 2018

Получите ваши данные из класса чтения типа.Затем определите функцию следующим образом:

    data EventType = RUN_EVENT Integer | GIVE_ITEM Integer Integer ... deriving (Show, Read) 

    stringToTerm :: String -> Integer -> EventType
    stringToTerm stringEvent n = read $ stringEvent ++ " " ++ show n

Имейте в виду, что аннотация типа важна из-за экземпляра чтения.Функция stringToTerm может быть частично применена, чтобы получить то, что вы хотите

С уважением

0 голосов
/ 12 июня 2018

Hacky решение, с -XViewPatterns для дополнительной ясности

stringToTerm [] = []
stringToTerm ("EVENT":(read -> num):xs) = EVENT num : stringToTerm xs
stringToTerm ("RUN_EVENT":(read -> num1):(read -> num2):xs) = RUN_EVENT num1 num2 : stringToTerm xs

Это позволяет вам не писать настоящий анализатор, тем самым избавляя вас от проблем, но взорвется при наличии неверных данных

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...