Scala 如何实现一元解析?

Scala 如何实现一元解析?,scala,parsing,monads,Scala,Parsing,Monads,我一直在理解Monad是如何工作的。我正在使用语法S->aSb编写解析器。输入为“a^n b^n for n>=0”,例如“aabb”;此解析器的返回值是布尔值 以前,我们使用此语法的临时变量。现在我想通过使用Monad而不是临时变量来实现这个解析器。但我试了很多次,仍然坚持这个问题 对象解析器{ 案例类解析器[+A](解析:List[Char]=>Option[(A,List[Char])){ def接受(l:List[Char]):布尔值 //如果解析后列表[Char]为空,则解析成功 =

我一直在理解Monad是如何工作的。我正在使用语法S->aSb编写解析器。输入为“a^n b^n for n>=0”,例如“aabb”;此解析器的返回值是布尔值

以前,我们使用此语法的临时变量。现在我想通过使用Monad而不是临时变量来实现这个解析器。但我试了很多次,仍然坚持这个问题


对象解析器{
案例类解析器[+A](解析:List[Char]=>Option[(A,List[Char])){
def接受(l:List[Char]):布尔值
//如果解析后列表[Char]为空,则解析成功
=parse(l).map(u._2.isEmpty).getOrElse(false)
//我不确定这是否正确
def flatMap[B](f:A=>Parser[B]):Parser[B]=Parser{
input=>parse(input).flatMap{case(x,midput)=>f(x).parse(midput)}
}
//map(i=>i+1)使用解析器a进行解析,并应用函数i+1
defmap[B](f:A=>B):解析器[B]=解析器{
输入=>解析(输入)匹配{
案例部分((t,剩余))=>部分((f(t,剩余))
案例=>无
}
}
//a.orElse(b)首先尝试使用解析器a
//如果a返回None,它将尝试解析器b
//如果b也返回None,则所有规则都将用尽
def orElse[B](that:Parser[B]):Parser[A,B]]=Parser{
输入=>解析(输入)匹配{
案例部分((p,剩余))=>部分((左(p,剩余))
case None=>that.parse(输入)匹配{
案例部分((p,剩余))=>部分((右(p,剩余))
案例无=>无
}
}
}
}
//不确定这是否正确
定义单位[T](x:T):解析器[T]=解析器{
_=>一些((x,List()))
}
//如果可能,消耗c
def char(c:char):解析器[Unit]=解析器{
案例x::如果c==x=>Some(((),rest))则为rest
案例=>无
}
def main(参数:数组[字符串]):单位={
vals:Parser[Int]
=char('a')。平面图{{u=>
S.flatMap{i=>
char('b')。映射{\u=>
i+1
}
}
}.orElse(单位(0)).map(u.merge)
S.accepts(“.toList)//true
S.accepts(“aaabbb.toList)//true
S.accepts(“aaa.toList)//false
S.accepts(“bbbaaa.toList)//false
}
}
通常,当我们说“单子解析”时,我们的意思是使解析器成为单子。我们写

class Parser[+A] { ... }
解析器[A]
接受输入并返回解析后的
A
,或者可能失败,或者可能会有一些输入剩余。让我们保持简单:一个
解析器[a]
接受一个
列表[Char]
,而
选项
会返回一个
a
,剩下的
列表[Char]

case class Parser[+A](parse: List[Char] => Option[(A, List[Char])]) {
  def accepts(l: List[Char]): Boolean
    = parse(l).map(_._2.isEmpty).getOrElse(false)
  // do not bother with the List('#') stuff
}
您可以使用组合器构建
解析器
a.flatMap(b)
是一个解析器,它匹配
a
,后跟
b

// case class Parser[+A](...) {
  def flatMap[B](f: A => Parser[B]): Parser[B] = Parser { input =>
    parse(input).flatMap { case (x, midput) => f(x).parse(midput) }
  }
// }
Parser.unit(x)
返回
x
而不消耗任何输入,这就是为什么
Monad
很重要的原因。您还应该有
映射
,它可以在不更改匹配内容的情况下更改返回值。您还需要一个用于交替的组合器。我会把这些留给你去实施

object Parser {
    def unit[T](x: T): Parser[T] = ???
}
// case class Parser[+A](...) {
    def map[B](f: A => B): Parser[B] = ???

    // left-biased greedy: if this parser succeeds (produces Some) then
    // that parser is never tried (i.e. no backtracking)
    // replacing Option with Seq is the easiest way to get backtracking
    // but we don't need it to define S
    def orElse[B](that: Parser[B]): Parser[Either[A, B]] = ???
// }
您还需要一些基本的
解析器来构建更复杂的解析器
Parser.char(x)
匹配单个char
x
,并且不返回任何有用的内容

// object Parser {
  def char(c: Char): Parser[Unit] = Parser {
    case x :: rest if c == x => Some(((), rest))
    case _ => None
  }
// }
然后,您可以非常自然地定义
S
。您甚至可以让解析器为匹配的
a
s/多少
b
s返回一个
Int

lazy val S: Parser[Int]
  = (for { _ <- Parser.char('a')
           i <- S
           _ <- Parser.char('b')
         } yield (i + 1)).orElse(Parser.unit(0)).map(_.merge)
// i.e
lazy val S: Parser[Int]
  = Parser.char('a').flatMap { _ =>
      S.flatMap { i =>
        Parser.char('b').map { _ =>
          i + 1
        }
      }
    }.orElse(Parser.unit(0)).map(_.merge)

S.accepts("".toList) // true
S.accepts("aaabbb".toList) // true
S.accepts("aaa".toList) // false
S.accepts("bbbaaa".toList) // false
lazy val S:Parser[Int]
=(对于{u
i+1
}
}
}.orElse(Parser.unit(0)).map(u.merge)
S.accepts(“.toList)//true
S.accepts(“aaabbb.toList)//true
S.accepts(“aaa.toList)//false
S.accepts(“bbbaaa.toList)//false

您不必移动
列表[Char]
S
的定义中,因为我们编写的组合词为您做了这件事,只留下语法本身的逻辑。

@Vincent
S
是一个解析器。它对应于您的类
AnBn
。我重命名它是因为您的注释:
S->aSb;S->ε
。而不是编写解析器对于原始的实际代码,解析器现在更像是数据,函数像
orElse
等。组合解析器来创建新的解析器。
orElse
是交替的组合器;
a.orElse(b)
是一个解析器,它尝试使用
a
进行解析,然后返回到
b
。它大致对应于您将在正则表达式中编写的
(除非我们没有回溯)。
orElse
用于构建最终的解析器
S
。当您调用
a.flatMap(b).accepts(cs)
,然后调用
a.parse(cs)
,如果成功返回
Some(a,cs2)
,则调用
b(a).parse(cs2)
被调用。
flatMap
不执行任何东西;它们构建一个
解析器
,它只是一个函数对象。执行只在
接受
解析
时发生。不能编写类似于
case对象和bn{def accepts(l:List[Char])=??的东西
使用
flatMap
,除非您想要
def accepts(l:List[Char])={val S=???/*我写的东西*/;S.accepts(l)}
。哦,对不起,它在REPL中工作,但在实际的
scala
文件中,它似乎需要
惰性