Scala 案例树变换

Scala 案例树变换,scala,tree,case,transformation,Scala,Tree,Case,Transformation,这是楼梯手册中的一个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 Ex

这是楼梯手册中的一个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")

这是可行的,但很乏味(我正在使用的实际类有几个case子类)。此外,我可能需要几个类似的转换。是否有更好的替代方法(可能使用高阶函数)?

因此您的
renameVar
版本必须知道两件事:它必须知道如何递归树和如何重命名变量

一个解决方案可能是将这两个问题分开。您可以使用来让每个类控制它如何执行递归;访问方法只涉及如何遍历树。当它遍历时,它可以通过一个处理实际工作的函数(在您的情况下重命名变量)

下面是一个传递转换函数的简单实现(在
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)
,以类似的方式定义其
doVisit
方法,它将不必修改
renameVar
(或任何其他转换函数,如
renameVar
)即可工作。

谢谢。这很有效。这可以进一步抽象(例如,在一个trait中,以便我可以在需要时混合该trait。)实现看起来足够通用,可以通过内省来处理。@dips,有很多方法可以采用这种模式,具体取决于您的需要以及您要定义所有部分的位置。如果不知道你想做什么,很难给出具体的建议,但你可以把它作为进一步探索的起点。