从字符串列表中读取数据

时间:2013-02-07 15:13:13

标签: haskell

我有这样的数据结构

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 

但我忘了,有些数字可能是数据结构中的字符串。 有没有一个简单的解决方案,或者我需要编写一个替代的读类型类?

3 个答案:

答案 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