我有这样的数据结构
data Something = Something Integer String String
我要转换
["Something", "3", "text", "42"]
到数据。
现在,我有
altRead :: Read a => [String] -> a
altRead = read . unwords . hack
where
hack = map (\x -> if isNumber x then x else "\"" ++ x ++ "\"")
isNumber = foldl (\b c -> isDigit c && b) True
但我忘了,有些数字可能是数据结构中的字符串。 有没有一个简单的解决方案,或者我需要编写一个替代的读类型类?
答案 0 :(得分:3)
有了你拥有的东西,你真的不需要把它变成类型类。你可以这样做:
readSomething :: [String] -> Maybe Something
readSomething [_, n, s1, s2] = Just $ Something (read n) s1 s2
readSomething _ = Nothing
或者,如果你想消除第一个词的歧义:
data Something = Something Integer String String
| SomethingToo String Integer
readSomething :: [String] -> Maybe Something
readSomething ["Something", n, s1, s2] = Just $ Something (read n) s1 s2
readSomething ["SomethingToo", s, n] = Just $ SomethingToo s (read n)
readSomething _ = Nothing
答案 1 :(得分:3)
你在一些词法标记上面写了一个小解析器。您无法真正实现自Read
以来的read :: Read a => String -> a
实例,并且您希望为[String] -> a
执行a == Something
。例如,可以利用已存在的Read
个实例来引导解析您的Integer
。
让我们试试吧。我们将从令牌列表中解析Something
。
import Safe -- gives us readMay :: Read a => String -> Maybe a
parseSomething :: [String] -> Maybe Something
parseSomething ("Something":strInt:stra:strb:_) =
do int <- readMay strInt
return $ Something int stra strb
parseSomething _ = Nothing
我们可以使用Maybe
作为Applicative
更紧凑地执行此操作
import Control.Applicative
parseSomething :: [String] -> Maybe Something
parseSomething ("Something":strInt:stra:strb:_) =
Something <$> readMay strInt <*> pure stra <*> pure strb
parseSomething _ = Nothing
真的,我们应该返回任何未使用的令牌,这样我们就可以继续解析了。
parseSomething :: [String] -> (Maybe Something, [String])
parseSomething ("Something":strInt:stra:strb:rest) =
(Something <$> readMay strInt <*> pure stra <*> pure strb, rest)
parseSomething rest = (Nothing, rest)
我将所有这些结构引入您的解析的原因是,这开始朝向像Parsec
这样的解析器组合器的空间。无论什么时候你需要一个复杂的Read
,它开始变得有用,看看Haskell中一些非常好的解析库。
答案 2 :(得分:1)
GHCI:
data Something = Something Integer String String deriving (Read, Show)
let somethingStrings = ["Something", "3", "text", "42"]
let escapeForSomething [a,b,c,d] = [a, b, "\""++c++"\"", "\""++d++"\""]
let something = read (unwords (escapeForSomething somethingStrings)) :: Something