【问题标题】:Functional Parser example in Haskell using GHCiHaskell 中使用 GHCi 的函数式解析器示例
【发布时间】:2014-12-07 11:19:57
【问题描述】:

我是学习 Haskell 的初学者。这是我在使用 GHCi 时遇到的问题。

p :: Parser (Char, Char)
p =  do x <- item
        item
        y <- item
        return (x,y)

item :: Parser Char
item =  P (\inp -> case inp of
                      []     -> []
                      (x:xs) -> [(x,xs)])

item是另一个解析器,其中item :: Parser Char,简单的item就是解析一个字符串

当我加载文件然后执行

parse p "abcdef"

然后显示一个执行:

*** Exception: You must implement (>>=)

有解决这个问题的办法吗?


更新信息:

解析器定义如下:

newtype Parser a =  P (String -> [(a,String)])

instance Monad Parser where
   return      :: a -> Parser a
   return v    =  P (\inp -> [(v,inp)])

   (>>=)       :: Parser a -> (a -> Parser b) -> Parser b
   p >>= f     = --...

【问题讨论】:

  • Parser 是如何定义的?您是否定义了它,或者您希望它由另一个库定义,如果是,是哪个库?

标签: haskell


【解决方案1】:

为了使用do 表示法,您的Parser 必须是Monad 的一个实例:

instance Monad Parser where
  return :: a -> Parser a
  return = -- ...
  (>>=) :: Parser a -> (a -> Parser b) -> Parser b
  p >>= f = -- ...

编译器需要你填写return&gt;&gt;=的定义。

do 表示法是一种语法糖,可用于 &gt;&gt;=(发音为“bind”)。例如,您的代码脱糖为:

p :: Parser (Char, Char)
p =  item >>= \x ->
     item >>= \_ ->
     item >>= \y ->
     return (x,y)

或者,使用更明确的括号:

p = item >>= (\x -> item >>= (\_ -> item >>= (\y -> return (x,y))))

&gt;&gt;= 描述了如何将Parser a 与函数a -&gt; Parser b 结合起来创建一个新的Parser b

使用您对Parser 的定义,一个工作的Monad 实例是

instance Monad Parser where
  return a = P $ \s -> [(a,s)]
  p >>= f = P $ concatMap (\(a,s') -> runParser (f a) s') . runParser p
  -- which is equivalent to
  -- p >>= f = P $ \s -> [(b,s'') | (a,s') <- runParser p s, (b,s'') <- runParser (f a) s']

考虑&gt;&gt;=p :: Parser a 和函数f :: a -&gt; Parser b 方面的作用。

  • 当展开时,p 接受 String,并返回 (a,String) 对的列表

    runParser p :: String -> [(a,String)]
    
  • 对于每个(a,String) 对,我们可以在a 上运行f 以获取新的解析器q

    map go . runParser p :: String -> [(Parser b,String)]
      where go :: (a, String) -> (Parser b, String)
            go (a,s') = let q = f a in (q, s')
    
  • 如果我们打开 q,我们会得到一个函数,它接受 String 并返回 (b, String) 对的列表:

    map go . runParser p :: String -> [(String -> [(b,String)],String)]
      where go :: (a, String) -> (String -> [(b,String)],String)
            go (a,s') = let q = f a in (runParser q, s')
    
  • 我们可以在与a 配对的String 上运行该函数,以立即获取我们的`(b, String) 对列表:

    map go . runParser p :: String -> [[(b,String)]]
      where go :: (a, String) -> [(b,String)]
            go (a,s') = let q = f a in runParser q s'
    
  • 如果我们将生成的列表列表展平,我们会得到一个 String -&gt; [(b,String)],它只是被解包的 Parser b

    concat . map go . runParser p :: String -> [(b,String)]
      where go :: (a, String) -> [(b,String)]
            go (a,s') = let q = f a in runParser q s'
    

【讨论】:

  • 谢谢,rampion。信息已更新并显示 Parser 的定义。那么如何在给定的示例中实现 >>=。
  • Tony Ngan:我和 user5402 要求的是 Parser 数据类型的定义,而不是 item 的定义。我想我可以从您对 P 构造函数的使用中推断出来,但如果我错了,请告诉我。
  • 我又更新了。 newtype Parser a = P (String -&gt; [(a,String)]) ,那么 s' 在您上面提到的 Monad 实例中是什么意思。
  • tony.0919:我已经充实了我对如何实现&gt;&gt;= 的描述。如果您对此有任何疑问,请告诉我。
猜你喜欢
  • 2016-10-12
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 2022-07-07
  • 2022-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多