4

我有一个单子类型Exp,我想构建一个解析器来解析成这样的值。以下代码有效,但我能做些更好/更酷的事情吗?

def grid(x: Int, y: Int): Problem = ???
def expInt: Parser[Exp[Int]] = ???

def grid: Parser[Exp[Problem]] =
  for{
    ex ~ _ ~ ey <- "grid(" ~> expInt ~ "," ~ expInt <~ ")"
  } yield for{
    x <- ex
    y <- ey
  } yield grid(x,y)

我听说过 monad 转换器,但还是有点害怕 Scalaz 7 奇怪的导入系统。有人可以回答是否

  1. 我可以使用单子变压器和
  2. 原则上这会是什么样子,将 Scalaz 7 用于包装我自己的ExpMonad 的 Scala 组合解析器。
4

1 回答 1

6

首先是简单的部分——“奇怪的”导入系统:

import scalaz._, Scalaz._

就这样。这将始终有效(当然,除非名称冲突,但这是任何库的可能性并且很容易解决),并且没有人会因为不使用主要是关于文档的新的 à la carte 导入而看不起你。

monad 转换器的基本模式是你有一个WhateverT[F[_], ...]类型类,它有一个名为runWhateverT(或只是run)返回一个F[Whatever[...]].

在这种情况下,您似乎想要以 结尾Parser[Exp],这表明您需要自己的ExpT[F[_]]变压器。我不会从如何实现这个细节开始(当然,这取决于你的 monad 的语义),但会给出一个使用 Scalaz 的例子OptionT

import scala.util.parsing.combinator._
import scalaz._, Scalaz._

object MyParser extends RegexParsers {
  implicit val monad = parserMonad(this)

  def oddNumber: OptionT[Parser, Int] = OptionT.optionT(
    "\\d+".r ^^ (_.toInt) ^^ (i => (i % 2 != 0) option i)
  )

  def pairOfOdds: OptionT[Parser, (Int, Int)] = for {
    _ <- literal("(").liftM[OptionT]
    x <- oddNumber
    _ <- literal(",").liftM[OptionT]
    y <- oddNumber
    _ <- literal(")").liftM[OptionT]
  } yield (x, y)

  def apply(s: String) = parse(pairOfOdds.run, s)
}

有关我们为什么需要这条线的一些讨论,请参阅我的问题here 。implicit val monad = ...

它是这样工作的:

scala> MyParser("(13, 43)")
res0: MyParser.ParseResult[Option[(Int, Int)]] = [1.9] parsed: Some((13,43))

scala> MyParser("(13, 42)")
res1: MyParser.ParseResult[Option[(Int, Int)]] = [1.8] parsed: None

请注意,oddNumber.run这将给我们一个Parser[Option[Int]]解析一串数字并返回 a Some(i)(如果它们表示奇数)或 a None(如果它们是偶数)。

但是,在这种情况下,我们实际上不会调用oddNumber.run—— 我们使用 monad 转换器这一事实意味着我们可以在单个理解中组合oddNumber提升Parser的动作( here)。literal(...)for

这里的语法很难看——例如,我们失去了漂亮的~组合子和从Stringto的隐式转换Parser[String],但如果我们愿意,我们可以轻松编写这些东西的提升版本。

于 2013-06-22T15:08:34.783 回答