【问题标题】:Pattern match on a generic type泛型类型的模式匹配
【发布时间】:2010-12-27 18:22:31
【问题描述】:

为什么我不能对 Node[T] 进行模式匹配?

object Visitor {
  def inorder[T](root: Node[T]) : Unit = {
    root match {
    case End => return;
    case Node[T] => {
      if( root.left != null )
          inorder( root.left )

      println( root.toString );
      inorder( root.right );
    }
    case _ => return;
    }
  }
}

更新

代码是来自99 scala problems的逐字复制

我收到了这个编译时错误:

BinaryTree.scala:25: '=>' expected but '[' found.
[error]         case Node[T] => {
[error]                  ^
[error] one error found

第 25 行指向该行

case Node[T] => {

【问题讨论】:

    标签: scala


    【解决方案1】:

    可以在某些情况下绑定类型参数。绑定此类类型参数可用于对模式匹配子句的主体进行类型检查——由于擦除,这些绑定会被删除,这意味着您无法在运行时区分它们(此类内容在 .NET CLR 中是可能的)。

    您的代码似乎没有使用该类型 - 您需要查看 Node、End 的定义。实现您想要的简单方法可能只是忽略 Node[T] 的类型参数是什么。下面是一个使用正确语法的示例。

    请注意,如果你想编写一个匹配类型(通用或非通用)的模式,它总是具有v : T 的形式。另一方面,如果您匹配案例类,则该模式具有C(p1...,pN) 的形状。语言规范包含所有详细信息。

    除了将类型绑定到类型变量a,我们还可以将其绑定到_,以强调我们不使用此类型。

    trait Tree[+T]
    abstract class Node[+T] extends Tree[T] {
       def left: Tree[T]
       def right: Tree[T]
    }
    object End extends Tree[Nothing]
    
    object Visitor {
      def inorder[T](root: Tree[T]) : Unit = {
        root match {
          case End => return;
          case node:Node[a] => {
            inorder( node.left ) // node.left has the type Tree[a]
            println( node.toString );
            inorder( node.right );
          }
          case _ => return;
        }
      }
    }
    

    【讨论】:

    • 我从不知道理论上可以在模式匹配中绑定Node[a] 中的类型。
    【解决方案2】:

    好吧,我通过解释器运行您的代码,我认为它与类型擦除无关。可能只是一些语法错误。试试这个:

    object Visitor {
      def inorder[T](root: Node[T]): Unit = {
        root match {
        case End => return;
        case n:Node[_] => {
          if( root.left != null )
              inorder( root.left )
    
          println( root.toString );
          inorder( root.right );
        }
        case _ => return;
        }
      }
    }
    

    您需要指明将绑定到匹配项的变量名称,例如n:Node[T]。这会给您一个关于类型擦除的警告,您可以使用n:Node[_] 将其删除。编译器可以根据root 的类型推断root.leftroot.right 的类型,所以类型擦除在这里并没有真正发挥作用......

    注意:我刚刚检查了规范,类型模式的语法是:

    varid ':' TypePat
    '_' ':' TypePat
    

    如果您提供来自编译器的实际错误消息以及 Node 和 End 的定义会有所帮助,否则我们需要推断所有这些内容。


    编辑:

    假设您正在编译http://aperiodic.net/phil/scala/s-99/tree.scala,您的代码中确实存在语法错误。使用n:Node[_]。然后你会得到一些类型错误。这对我有用:

    import binarytree._
    object Visitor {
      def inorder[T](root: Tree[T]) : Unit = {
        root match {
        case End => return;
        case n:Node[_] => {
          if( n.left != null )
              inorder( n.left )
          println( n.toString );
          inorder( n.right );
        }
        case _ => return;
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      为了在运行时为 Scala 中的泛型匹配具体化类型,您需要使用 Manifest (q.v.) 编写代码的方式,类型被擦除。

      【讨论】:

      • 不,问题不在于类型擦除,因为他没有尝试匹配擦除的类型。 @huynhjl 是对的,可能只是语法问题; "case _: Node[T] =>" 必须使用,因为 Node[T] 是一种类型。 "case End =>" 很好,因为 End 是一个值。
      • 重读代码,我认为 Alex 是对的:Frank 看到的问题是句法问题,但我没有足够的 Frank 代码来尝试。
      【解决方案4】:

      这种行为称为type erasure。使用Manifests,您可以解决问题。请参阅this question 了解更多信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-20
        • 2019-06-03
        • 2016-10-29
        • 2016-09-04
        • 2011-10-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多