p >> = f在解析器实例中如何工作?

时间:2018-08-23 14:02:40

标签: parsing haskell

class Monad m where
    return :: a -> m a
    (>>=) :: m a -> (a -> m b) -> m b

instance Monad Parser where
    return a = Parser (\cs -> [(a,cs)])
    p >>= f = Parser (\cs -> concat [parse (f a) cs’ | (a,cs’) <- parse p cs])

paper

有一个解释
  

(>> =)运算符是解析器的排序运算符。用一个   由parse(Parser p)= p定义的解析器的解构函数   解析器p >> = f首先将解析器p应用于参数字符串   cs给出形式(a,cs’)的结果列表,其中a是一个值   而cs是一个字符串。对于每个这样的对,f a是一个解析器,它是   应用于字符串cs'。结果是列表列表,即   然后连接起来以给出最终结果列表。

但这还不清楚。谁能举例说明?非常感谢。

1 个答案:

答案 0 :(得分:1)

在Haskell中,解析器是Monad的示例。 Monad实际上是一个比解析器更广泛的话题,但实际上它们对这个概念做了很好的介绍。这是这里的工作方式。

在解析器中,您希望能够识别类似(在BNF中)的规则

assignment ::= identifier "=" expression

在Haskell中,您应该这样写:

data Statement = 
    Assignment Identifier Expression
    | Block [Statement]  -- A block is a list of statements.
    | Conditional Expression Statement Statement
    | -- etc. 

identifier :: Parser Identifier  -- Implementation omitted.

literal :: String -> Parser ()   -- Implementation omitted.

expression :: Parser Expression   -- Implementation omitted.

assignment :: Parser Statement
assignment = do          -- Note the "do" here.
   ident <- identifier   
   literal "="          
   expr <- expression
   return (Assignment ident expr)   -- "return" doesn't mean what you think.

这捕获了解析器的思想,即分配是一系列子解析器:首先解析标识符,然后识别“ =”,然后解析右侧的表达式。希望您能看到BNF如何映射到Haskell代码中。

Haskell中的“ do”语法是使用>>=运算符(称为“ bind”)的表达式的语法糖。上面的assignment示例将糖分解为以下内容(大约:我在这里跳过了有关模式匹配失败的内容)

assignment = 
    identifier >>= (\ident ->
        operator "=" >>= (\dummy ->
           expression >>= (\expression -> return (Assignment ident expr))))

每个\(称为“ lambda”)都引入了一个匿名函数。 >>=运算符采用两个参数。左侧是包裹在Parser monad中的一些值。右侧是一个函数,该函数接受此值并返回新的包装值。绑定运算符的工作是解开左侧的值(这可能涉及做一些魔术上的副作用)并将其传递给右侧的函数。在这种情况下,魔术的副作用包括消耗输入文本。

请注意绑定和匿名函数如何嵌套在已删除版本中。 “ do”语法中的每一行都将转换为上一行中的一个新函数。这意味着到目前为止,最后一个函数可以访问所有函数中的所有变量。它是一种使用像Haskell这样的纯函数式语言对一组作业进行建模的方法。

我在评论中说,“返回”并不意味着您认为的含义。在Haskell中,它与控制流无关,它只是将一个值包装为monad(在这种情况下为Parser),而不会引起任何副作用。

此特定的解析器生成结果列表。也就是说,当遇到任何歧义时,它会生成一个结果列表,而不是在每个步骤上都确定一个真实解析。绑定运算符将每个结果取到最远,然后将其传递给右边的函数,这又可能再次生成多个结果。否则解析器可能不会产生任何结果,在这种情况下该分支将被放弃。因此,每个步骤都从结果列表开始,然后下一步给出结果列表的列表,然后通过“ concat”功能将其折叠成平坦的结果列表。

您的问题中提供的Monad类是标准库的一部分。碰巧有很多有用的功能适用于所有monad,因此为它们提供一个通用的接口就可以很好地工作。对于Parser,绑定运算符的类型为

(>>=) :: Parser a -> (a -> Parser b) -> Parser b

return具有类型

return :: a -> Parser a

沉思这些类型以及我对绑定和返回所做的描述,您可能会获得一元启示。