了解Read实例

时间:2017-06-24 07:12:46

标签: parsing haskell

我制作了ReadShow个数据实例,但不了解Read个实例

data Tests = Zero
          | One Int
          | Two Int Double

instance Show Tests where
  show Zero      = "ZERO"
  show (One i)   = printf "ONE %i" i
  show (Two i j) = printf "TWO %i %f" i j

instance Read Tests where
  readsPrec _ str   = [(mkTests str, "")]

mkTests :: String -> Tests
mkTests = check . words

check :: [String] -> Tests
check ["ZERO"]      = Zero
check ["ONE", i]    = One (read i)
check ["TWO", i, j] = Two (read i) (read j)
check _             = error "no parse"

main :: IO ()
main = do
  print Zero
  print $ One 10
  print $ Two 1 3.14

  let x = read "ZERO" :: Tests
  print x
  let y = read "ONE 2" :: Tests
  print y
  let z = read "TWO 2 5.5" :: Tests
  print z

这是输出

ZERO         
ONE 10       
TWO 1 3.14   
ZERO         
ONE 2        
TWO 2 5.5  

以下是问题:

  1. 实施Read实例的推荐方法是什么?

    • Read类的最小完整定义为readsPrec | readPrecreadPrec :: ReadPrec a描述写道
      

    建议使用新式解析器替换readsPrec(仅限GHC)。

    • 我应该使用readPrec代替吗?我无法在网上找到任何我能理解的例子。
    • 什么是new-style parsers,是parsec
  2. readsPrec :: Int -> ReadS a 的第一个Int参数用于什么?

  3. 无论如何从Read以某种方式得出Show

  4. 过去我可以使用deriving (Show,Read)来完成大部分工作。但是这一次我想进入下一个阶段。

1 个答案:

答案 0 :(得分:-1)

  1. 在我看来,实现Read的正确方法是派生它,否则,最好转向更复杂的解析器。无论如何,这里是你所有问题的答案。
    • readPrec是GHC提供的基于解析器组合的简单方法。如果您愿意牺牲Read实例的可移植性,您可以使用它,这使解析变得更容易。
    • 我在
    • 中包含了一个如何使用readPrec的小例子
    • parsec与readPrec不同,但两者都是解析器组合器。 Parsec是一个更完整的解析器库。另一个解析器组合器库是attoparsec,其工作方式与parsec非常相似。
    • parsec和attoparsec不能与普通的读类型类一起使用(至少直接使用),但它们提供的更大灵活性使它们成为您想要更复杂解析的好主意。
  2. Int的{​​{1}}参数用于在解析时处理优先级。当您想要解析算术表达式时,这可能很重要。如果优先级高于当前运算符的优先级,则可以选择无法解析。
  3. 遗憾的是,readsPrecRead导出Show
  4. 以下是一些代码段,展示了如何使用Read实现ReadPrec

    ReadPrec示例:

    instance Read Tests where
      readPrec = choice [pZero, pOne, pTwo] where
        pChar c = do
          c' <- get
          if c == c'
          then return c
          else pfail
        pZero = traverse pChar "ZERO" *> pure Zero
        pOne = One <$> (traverse pChar "ONE " *> readPrec)
        pTwo = Two <$> (traverse pChar "TWO " *> readPrec) <*> readPrec
    

    一般来说,实现Read比不那么重量级的解析器更不直观。根据你要解析的内容,我强烈建议学习parsec或attoparsec,因为当你想要解析更复杂的东西时它们非常有用。