1

別のノードを(経由で)指すことができるノードクラスがありますnext。そのノードは、さまざまな階層でサブクラス化できます。次に、単一リンクリストのイメージで、これらのさまざまなノードのチェーンを作成できます。次に、そのリスト内の任意のノードから始めて、特定のタイプの最初のノードを検索します。したがって、私はその問題のために型パラメータ化された関数を作成しますcheck。しかし、正しい一致を見つけることができません。これは、Scalaワークシートから抜粋した完全な出力コードの例です。

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]
}

(これは防弾ではないことに注意してください:部分関数に与えられた引数が多形でない場合にのみうまく機能します。その理由は、一般に、渡される引数に使用できるマニフェストがないためです。型が消去されたクラスですが、すべてのノードクラスが多形ではないため、これが問題になることはありません。)

これら2つの機能を組み合わせることで、目的の結果が得られるはずです。


ケースに適応できる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 に答える