0

我有这样的数据结构

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 

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

4

3 回答 3

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
于 2013-02-07T15:26:29.273 回答
3

您正在一些词法标记之上编写一个小型解析器。你不能真正实现一个Read实例,因为read :: Read a => String -> a你想要[String] -> aa == 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我们也可以使用as更紧凑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 中一些非常好的解析库就开始变得有用了。

于 2013-02-07T21:55:04.070 回答
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
于 2013-02-07T16:17:37.500 回答