首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Haskell: Graham图书解析(Ch-8):“解析(f,v)输出”是什么,它是如何完成的?

Haskell: Graham图书解析(Ch-8):“解析(f,v)输出”是什么,它是如何完成的?
EN

Stack Overflow用户
提问于 2018-09-25 11:56:50
回答 2查看 379关注 0票数 3

我的问题是关于格雷厄姆·赫顿的书“http://www.cs.nott.ac.uk/~pszgmh/book-old.html”。

在8.4节中创建了一个解析器,我假设任何回答的人都有这本书,或者可以在上面的链接中看到幻灯片8的链接。

名为item的基本解析器被描述为:

代码语言:javascript
运行
复制
type Parser a = String -> [(a, String)]

item :: Parser Char

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

它与do一起使用来定义另一个解析器p ( do解析器)。

代码语言:javascript
运行
复制
p :: Parser (Char, Char)

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

有关的约束定义是:

代码语言:javascript
运行
复制
(>>=) :: Parser a -> (a -> Parser b) -> Parser b

p >>= f = \inp -> case parse p inp of
                       [] -> []
                       [(v,out)] -> parse (f v) out

return被定义为:

代码语言:javascript
运行
复制
return  :: a -> Parser a

return v = \inp -> [(v,inp)]

parse被定义为:

代码语言:javascript
运行
复制
parse :: Parser a -> String -> [(a,String)]

parse p inp = p inp

程序( do解析器)接受一个字符串,并选择第一个和第三个字符,并以元组的形式返回它们,其中字符串的其余部分在列表中,例如,"abcdef"生成[('a','c'), "def"]

我想知道(f v) out[(v,out)] -> parse (f v) out中如何返回一个解析器,然后应用于out

  • fdo解析器中是itemitem接受字符'c'返回[('c',[])]吗?
  • 这怎么可能是一个解析器,它如何将out作为参数呢?

也许我只是不明白(f v)是做什么的。

  • 另外,当再次调用do时,item解析器如何“丢弃”每次返回的值,以便对输入字符串的其余部分进行操作?
  • 通过do解析器工作的对象是什么,在每一步是如何修改的,以什么方式改变它?
EN

回答 2

Stack Overflow用户

发布于 2018-09-25 12:55:55

f v生成Parser b,因为fa -> Parser b类型的函数,而va类型的值。然后用这个parse和字符串out作为参数调用out

“do”解析器中的f是项。

不,不是的。让我们考虑一下解析器的一个简化版本(尽管现在有点没有意义):

代码语言:javascript
运行
复制
p = do x <- item
       return x

这将有损于:

代码语言:javascript
运行
复制
p = item >>= \x -> return x

因此,>>=的右操作数,即f,是\x -> return x,而不是item

另外,每次调用item时,“do”解析器如何“删除”返回的值,以便对输入字符串的其余部分进行操作?通过“do”解析器工作的对象是什么?它是如何改变的,每一步都是如何改变的,通过什么方法改变了它?

应用解析器时,它返回包含解析值的元组和表示输入其余部分的字符串。例如,如果您查看item,元组的第二个元素将是xs,它是输入字符串的尾部(即包含输入字符串中除第一个字符以外的所有字符的字符串)。元组的第二部分将作为后续解析器的新输入(按照[(v,out)] -> parse (f v) out)提供,这样,每个连续解析器都将前一个解析器生成的字符串作为其输出元组的第二部分(这将是其输入的后缀)作为输入。

针对你的评论:

当您编写"p = item >>= \x ->返回x“时,这是否等同于第一行"p = do <- item"?

不,它相当于整个do-block (即do {x <- item; return x})。你不能像这样逐行翻译do-blocks .do { x <- foo; rest }等同于foo >>= \x -> do {rest},因此您将始终将do-block的其余部分作为>>=的右操作数的一部分。

但这并不能简化为简单地将“out”作为下一行的输入。如果“do”解析器的下一行是项解析器,那么解析在做什么?

让我们来看一个例子,其中我们调用了两次item (这类似于您的p,但没有中间项)。在下面,我将使用===来表示===上面和下面的表达式是等价的。

代码语言:javascript
运行
复制
do x <- item
   y <- item
   return (x, y)
=== -- Desugaring do
item >>= \x -> item >>= \y -> return (x, y)
=== -- Inserting the definition of >>= for outer >>=
\inp -> case parse item inp of
             [] -> []
             [(v,out)] -> parse (item >>= \y -> return (v, y)) out

现在,让我们将其应用于输入"ab":

代码语言:javascript
运行
复制
case parse item "ab" of
     [] -> []
     [(v,out)] -> parse (item >>= \y -> return (v, y)) out
=== Insert defintiion of `parse`
case item "ab" of
     [] -> []
     [(v,out)] -> parse (item >>= \y -> return (v, y)) out
=== Insert definition of item
case ('a', "b") of
     [] -> []
     [(v,out)] -> parse (item >>= \y -> return (v, y)) out
===
parse (item >>= \y -> return ('a', y)) out

现在,我们可以扩展第二个>>=,就像我们最初做的那样,最终得到('a', 'b')

票数 4
EN

Stack Overflow用户

发布于 2019-02-19 03:06:19

我在阅读语法时遇到了类似的问题,因为这并不是我们所习惯的。

代码语言:javascript
运行
复制
(>>=) :: Parser a -> (a -> Parser b) -> Parser b

p >>= f = \inp -> case parse p inp of
                       [] -> []
                       [(v,out)] -> parse (f v) out

因此,对于这个问题:

我想知道(f v) out[(v,out)] -> parse (f v) out中如何返回一个解析器,然后应用于out

这是因为这是第二艺术( f):(>>=) :: Parser a -> (a -> Parser b) -> Parser b .)的签名。f接受一个a并生成一个Parser bParser b采用String,即out . (f v) out.

但是它的输出不应该与我们正在编写的函数的输出相混淆:>>=

  • 我们正在输出一个解析器. (>>=) :: Parser a -> (a -> Parser b) -> (>>=) :: Parser a -> (a -> Parser b) ->
  • 我们输出的Parser有包装和链接前两个arg的工作。

解析器是一个需要1 arg的函数。这是在第一个=之后建造的.也就是说,通过返回一个(匿名)函数:= \inp -> ...inp引用了我们正在构建的解析器的输入字符串

所以剩下的就是定义构造函数应该做什么..。注意:我们没有实现任何一个输入解析器,只是将它们链接在一起.所以输出解析器函数应该是

  • 将输入解析器(p)应用于其输入(inp):p >>= f = \inp -> case parse p inp of
  • 以解析v的输出为结果,out是输入的剩余部分。
  • 应用输入函数( (a -> Parser b))是f)到解析的结果(v)
  • Parser b (一个需要1 arg的函数)
  • 因此,将该输出解析器应用于第一个解析器(out)之后的其余输入。

对我来说,理解在于使用析构,并认识到我们正在构建一个函数,它将其他函数的执行结合在一起,仅仅考虑到它们的接口。

希望能帮上忙..。它帮助我写到:-)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52497671

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档