1

これが階段の本のExprクラスです。

abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

ここで、式の変数の名前を変更する関数が必要です。これが私の最初の試みです。

def renameVar(expr: Expr, varName: String, newName: String): Expr = expr match {
    case Var(name) if name == varName => Var(newName)
    case Number(_) => expr
    case UnOp(operator, arg) => UnOp(operator, renameVar(arg, varName, newName))
    case BinOp(operator, left, right) => BinOp(operator, renameVar(left, varName, newName), renameVar(right, varName, newName))
}

val anExpr = BinOp("+", Number(1), Var("x"))
val anExpr2 = renameVar(anExpr, "x", "y")

これは機能しますが、面倒です(私が使用している実際のクラスには、いくつかのケースサブクラスがあります)。また、いくつかの同様の変換が必要になる場合があります。より良い代替案はありますか(おそらく高階関数を使用します)?

4

1 に答える 1

2

したがって、のバージョンはrenameVar2つの別個のことを知っている必要があります。ツリーを再帰する方法と、変数の名前を変更する方法を知っている必要があります。

1つの解決策は、これら2つの懸念を分離することかもしれません。ビジターデザインパターンを使用して、各クラスが再帰を実行する方法を制御できるようにすることができます。訪問方法は、ツリーをトラバースする方法のみに関係します。トラバースするときに、実際の作業を処理する関数を通過できます(この場合は変数の名前を変更します)。

Exprこれは、変換関数(を操作して返す)を渡す単純な実装ですExpr。を使用するという事実PartialFunctionにより、操作するツリー内の式をパターンマッチングすることができます。ケースでカバーされていない式は、(で指定されているようにdoVisit)通常の再帰にフォールバックします。

さまざまなタスクによっては、より複雑な訪問方法が必要になる場合があります。しかし、これはあなたに方向性のアイデアを与えるはずです:

// Class Hierarchy
abstract class Expr {
  def visit(f: PartialFunction[Expr, Expr]): Expr = if (f.isDefinedAt(this)) f(this) else doVisit(f)
  protected def doVisit(f: PartialFunction[Expr, Expr]): Expr
}
case class Var(name: String) extends Expr {
  protected def doVisit(f: PartialFunction[Expr, Expr]) = this
}
case class Number(num: Double) extends Expr {
  protected def doVisit(f: PartialFunction[Expr, Expr]) = this
}
case class UnOp(operator: String, arg: Expr) extends Expr {
  protected def doVisit(f: PartialFunction[Expr, Expr]) = UnOp(operator, arg.visit(f))
}
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr {
  protected def doVisit(f: PartialFunction[Expr, Expr]) = BinOp(operator, left.visit(f), right.visit(f))
}

// Transformation Functions
def renameVar(expr: Expr, varName: String, newName: String): Expr = {
  expr.visit { case Var(`varName`) => Var(newName) }
}

これで、のような新しいクラスを導入して、同様の方法でTernaryOp(String, Expr, Expr, Expr)そのメソッドを定義できます。これは、変更(またはのような他の変換関数)を必要とせずに機能します。doVisitrenameVarrenameVar

于 2012-04-25T05:04:16.667 に答える