如何在Haskell中将字符串转换为实际的术语?

时间:2018-06-12 15:22:53

标签: haskell functional-programming

我定义了以下数据类型

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

从某些测试中,我检索数据如下

  

[&#34; EVENT&#34;&#34; 6001&#34;&#34; E&#34;&#34; RUN_EVENT&#34;&#34; 6010&#34;] < / p>

当我处理数据时,例如,在&#34; E&#34;之后,我得到&#34; RUN_EVENT&#34;,我想构建类型为 EventType <的数据/ em>具有值 RUN_EVENT 6010

要做到这一点,我可以创建一个巨大的列表,基本上说如果我面对&#34; RUN_EVENT&#34;然后构造一个值为 RUN_EVENT 的值,但这不会有效,所以有没有办法转换&#34; RUN_EVENT&#34;到术语 RUN_EVENT ,以便我可以将其用作

exec = (stringToTerm "RUN_EVENT") 6010

4 个答案:

答案 0 :(得分:3)

使用Read

这是一个令人讨厌的hacky解决方案
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解决方案现在代表,但如果看起来它变得一团糟,那么就转换到正式的解析器。

答案 1 :(得分:1)

从Read类中派生您的数据。然后按如下方式定义函数

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

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

请记住,由于读取实例,类型注释很重要。函数stringToTerm可以部分应用于获得你想要的东西

此致

答案 2 :(得分:0)

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

这样你就不会写一个真正的解析器,从而省去了麻烦,但如果数据不正确就会爆炸

答案 3 :(得分:0)

直接实施您的问题可能是:

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

这显示了适用的风格。