【问题标题】:How to pattern match the parent trait (or class, or object) of case classes?如何模式匹配案例类的父特征(或类或对象)?
【发布时间】:2020-11-28 11:51:03
【问题描述】:

我试图在单个 case 子句中匹配所有二元运算符,但以下代码给出了错误:

object BinOp is not a case class, nor does it have a valid unapply/unapplySeq member
Note: def unapply(a: AST.this.Expr, b: AST.this.Expr): Option[(AST.this.Expr, AST.this.Expr)] exists in object BinOp, but it cannot be used as an extractor as it has more than one (non-implicit) parameter.

遍历树的核心代码:

tree match {
    case ... other ... cases
    case BinOp(a, b) => traverse(a), traverse(b)
}

AST类如下:

sealed trait Expr

case class Num(value: java.lang.Number) extends Expr

sealed trait BinOp extends Expr {
  val a, b: Expr
}

object BinOp {
  def unapply(a: Expr, b: Expr): Option[(Expr, Expr)] = Some(a, b)
}

case class Add(a: Expr, b: Expr) extends BinOp

case class Sub(a: Expr, b: Expr) extends BinOp

case class Mul(a: Expr, b: Expr) extends BinOp

case class Div(a: Expr, b: Expr) extends BinOp

为了便于说明,对代码段进行了极大的简化。

【问题讨论】:

  • 我实际上是在寻找一个通用的解决方案来遍历包含另一个节点的任何节点;闻起来像 Applicative/Functor 或其他一些深奥的 Haskell 概念。任何“可遍历”节点都将被匹配,例如
  • viyps,如果您将该评论添加为问题的编辑会更好
  • 您能否提供更多关于您将如何遍历这些节点的上下文?你只是想让它们可迭代还是什么?

标签: scala pattern-matching abstract-syntax-tree case-class


【解决方案1】:

错误消息似乎信息丰富

def unapply(a: AST.this.Expr, b: AST.this.Expr): Option[(AST.this.Expr, AST.this.Expr)] 
exists in object BinOp, but it cannot be used 
as an extractor as it has more than one (non-implicit) parameter.

所以unapply方法应该用单个参数定义,这样说

object BinOp {
  def unapply(binOp: BinOp): Option[(Expr, Expr)] = Some(binOp.a, binOp.b)
}

【讨论】:

    【解决方案2】:

    您似乎对提取器对象的工作方式存在误解。您可以将unapply 视为apply 的对偶,尽管并非总是如此。

    假设您没有将Add 作为一个案例类,并且已经为您创建了apply 方法,而是将其设为一个普通类并将apply 方法放在一个伴随对象中:

    object Add {
      def apply(a: Expr, b: Expr): Add = ???
    }
    

    unapply 方法就是这样,但输入和输出类型已切换(在 Option 内部)。

    //Could also be a Some[(Expr,Expr)], as far as I can tell
    def unapply(add: Add): Option[(Expr, Expr)] = (add.a, add.b)
    

    由于apply方法的输出只是一个值,你的unapply方法的输入是一个值,并且由于apply方法有多个输入,unapply方法有多个输出,或通过使用元组假装这样做。还有更多内容,但文档中的this page 将比我更深入。

    编辑:@jwvh 指出您不需要从unapply 方法返回Option - 它可以是任何类型的isEmpty 和@987654338 方法@。


    由于您还提到要对包含其他节点的任何类型的节点进行抽象,所以我想指出,如果您要使用模式匹配,您仍然必须创建自己的 @每种节点的 987654339@ 方法(或使其成为案例类)。

    【讨论】:

    • 谢谢,现在很清楚了!我最终使 Expr 成为 Iterable 的扩展,因此每种类型的节点(如 BinOp)都可以提供它的遍历方式。
    • 只是一个兴趣点:unapply() 返回类型必须具有isEmpty 方法,以指示匹配失败/成功,以及get 方法,以访问匹配的值。 Option 两者都提供,所以这通常很方便。否则unapply() 可以免费退货。它不必是 apply() 工厂方法的反映(但通常是)。
    • @jwvh 感谢您的信息,我一直认为它必须是一个选项或一些。
    猜你喜欢
    • 2020-11-19
    • 2013-05-28
    • 2011-11-27
    • 1970-01-01
    • 2012-01-02
    • 1970-01-01
    • 2015-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多