1

我有一个节点类,它可以指向另一个节点(通过next)。该节点可以在不同的层次结构中进行子类化。然后,我可以在单链表的图像中拥有这些不同节点的链。然后,从该列表中的任意节点开始,我想搜索特定类型的第一个节点。因此,我为此创建了一个类型参数化的函数check。但它未能找到正确的匹配。下面是完整的输出代码示例,取自 Scala Worksheet:

class A (i: Int) {
    var next: Option[A] = None
    def + (a: A) = {
      next = Some(a)
      a
    }
    override def toString = super.toString + "[" + i + "]"
}
class B (i: Int) extends A (i)
class C (i: Int) extends B (i)
class D (i: Int) extends B (i)

object test {

    val start = (new A(0))                    //> start  : A = A@e80a59[0]
    val test = start + (new A(1)) + (new B(2)) + (new C(3)) + (new D(4)) + (new A(5)) + (new C(6))
                                                  //> test  : A = C@5d173[6]

    def check[T <: B](a: A): Option[B] = {
        println("starting search for " + a)
        a match {
          case t: T =>
            println("SUCCESS, found=" + t)
            Some(t)
          case wrong =>
            println("did not find! wrong=" + wrong)
            a.next match {
            case Some(nxt) =>
                println("checking next=" + nxt)
                check[T](nxt)
            case _ =>
                println("SEARCH FAILED")
                None
          }
        }
    }                                         //> check: [T <: B](a: A)Option[B]

    /* correct - i expect this */
    println(check[C](new A(1)))               //> starting search for A@1f9dc36[1]
                                                  //| did not find! wrong=A@1f9dc36[1]
                                                  //| SEARCH FAILED
                                                  //| None
    /* correct - i expect this */
    println(check[D](new A(2)))               //> starting search for A@e86da0[2]
                                                  //| did not find! wrong=A@e86da0[2]
                                                  //| SEARCH FAILED
                                                  //| None
    /* incorrect - it must fail looking for C */
    println(check[C](new B(4)))               //> starting search for B@1754ad2[4]
                                                  //| SUCCESS, found=B@1754ad2[4]
                                                  //| Some(B@1754ad2[4])
    println(check[B](start))                  //> starting search for A@e80a59[0]
                                                  //| did not find! wrong=A@e80a59[0]
                                                  //| checking next=A@fe64b9[1]
                                                  //| starting search for A@fe64b9[1]
                                                  //| did not find! wrong=A@fe64b9[1]
                                                  //| checking next=B@186db54[2]
                                                  //| starting search for B@186db54[2]
                                                  //| SUCCESS, found=B@186db54[2]
                                                  //| Some(B@186db54[2])
    /* incorrect - it must find C(3) instead */
    println(check[C](start))                  //> starting search for A@e80a59[0]
                                                  //| did not find! wrong=A@e80a59[0]
                                                  //| checking next=A@fe64b9[1]
                                                  //| starting search for A@fe64b9[1]
                                                  //| did not find! wrong=A@fe64b9[1]
                                                  //| checking next=B@186db54[2]
                                                  //| starting search for B@186db54[2]
                                                  //| SUCCESS, found=B@186db54[2]
                                                  //| Some(B@186db54[2])
    /* incorrect - it must find D(4) instead */
    println(check[D](start))                  //> starting search for A@e80a59[0]
                                                  //| did not find! wrong=A@e80a59[0]
                                                  //| checking next=A@fe64b9[1]
                                                  //| starting search for A@fe64b9[1]
                                                  //| did not find! wrong=A@fe64b9[1]
                                                  //| checking next=B@186db54[2]
                                                  //| starting search for B@186db54[2]
                                                  //| SUCCESS, found=B@186db54[2]
                                                  //| Some(B@186db54[2])

问题是:

  1. 为什么会这样失败?它忽略传递的类型。

  2. 我怎样才能达到我想要的?

更新:根据建议,我尝试使用 Scala“清单”来与这里所谓的“类型擦除”作斗争,方法是执行此手动显式具体化。但是,我无法在清单的帮助下进行模式匹配 - 它根本无法以我能想到的任何方式使用它,我也无法在网上找到任何工作示例。

4

2 回答 2

1

以下似乎有效。不知道还能说什么,因为我不知道还有什么其他(可能更好)的方式来实现你想做的事情。我研究了Manifest一段时间的API来想出这个。另外我不知道这是否也适用于“ClassManifest”。

并注意子类型,因为我认为这段代码会失败。

scala> trait A
defined trait A        
scala> case class A1() extends A
defined class A1    
scala> case class A2() extends A
defined class A2

scala> def check[T <: A](a: A)(implicit m: Manifest[T]) = a.getClass == m.erasure
check: [T <: A](a: A)(implicit m: Manifest[T])Boolean

scala> check[A1](A2())
res7: Boolean = false
scala> check[A2](A2())
res8: Boolean = true
于 2012-09-28T12:36:21.983 回答
1

首先,我建议您将任务拆分为:

  1. 搜索具有特定属性的节点,以及
  2. 将属性指定为某个类的子类。

例如,Scala 集合有方法

def collectFirst[B](pf: PartialFunction[A, B]): Option[B]

找到为其定义函数的第一个元素并将函数应用于它。在您的情况下,您将在节点上而不是在其元素上使用部分函数。

然后,您可以创建一个函数,在给定(隐式)清单的情况下创建这样的部分函数:

def subclass[A <: AnyRef](implicit m: Manifest[A]): PartialFunction[Any,A] = {
  // Works properly only for arguments that have non-polymorphic types!
  case x if m.erasure.isInstance(x)  => x.asInstanceOf[A]
}

(请注意,这不是万无一失的:只有当赋予部分函数的参数不是多态的时,它才能正常工作。原因是通常我们没有可用于传递给它的参数的清单,只有他们的类型擦除类。但是由于您的所有节点类都不是多态的,所以这应该不是问题。)

通过结合这两个功能,你应该得到想要的结果。


我将给出一个完整的 Scala 集合示例,您可以根据自己的情况进行调整:

import scala.reflect._

object Test extends App {
  // Scala collections have this method:
  // def collectFirst[B](pf: PartialFunction[A, B]): Option[B]

  def subclass[A <: AnyRef](implicit m: Manifest[A]): PartialFunction[Any,A] = {
    // Works properly only for arguments that have non-polymorphic types!
    case x if m.erasure.isInstance(x)  => x.asInstanceOf[A]
  }

  def collectFirst[T <: AnyRef](xs: Seq[AnyRef])(implicit m: Manifest[T]) =
    xs.collectFirst(subclass[T]);

  val s: Seq[AnyRef] = Seq(
    new java.lang.Object(),
    "a",
    3 : java.lang.Integer,
    '@' : java.lang.Character
  );

  println(collectFirst[String](s));
  println(collectFirst[AnyRef](s));
  println(collectFirst[java.lang.Character](s));
  println(collectFirst[java.lang.Integer](s));
}
于 2012-09-28T13:02:16.150 回答