Scala 如何实现一元解析?
我一直在理解Monad是如何工作的。我正在使用语法S->aSb编写解析器。输入为“a^n b^n for n>=0”,例如“aabb”;此解析器的返回值是布尔值 以前,我们使用此语法的临时变量。现在我想通过使用Monad而不是临时变量来实现这个解析器。但我试了很多次,仍然坚持这个问题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]为空,则解析成功 =
对象解析器{
案例类解析器[+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)
匹配单个charx
,并且不返回任何有用的内容
// 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
的定义中,因为我们编写的组合词为您做了这件事,只留下语法本身的逻辑。@VincentS
是一个解析器。它对应于您的类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
文件中,它似乎需要惰性
。