4

Scala 2.8 コレクションのアーキテクチャーに関する非常に興味深い記事を読み、それを少し試してみました。まず、素晴らしいRNA例の最終的なコードを単純にコピーしました。ここに参照用があります:

abstract class Base
case object A extends Base
case object T extends Base
case object G extends Base
case object U extends Base

object Base {
  val fromInt: Int => Base = Array(A, T, G, U)
  val toInt: Base => Int = Map(A -> 0, T -> 1, G -> 2, U -> 3)
}

final class RNA private (val groups: Array[Int], val length: Int)
    extends IndexedSeq[Base] with IndexedSeqLike[Base, RNA] {

  import RNA._

  // Mandatory re-implementation of `newBuilder` in `IndexedSeq`
  override protected[this] def newBuilder: Builder[Base, RNA] =
    RNA.newBuilder

  // Mandatory implementation of `apply` in `IndexedSeq`
  def apply(idx: Int): Base = {
    if (idx < 0 || length <= idx)
      throw new IndexOutOfBoundsException
    Base.fromInt(groups(idx / N) >> (idx % N * S) & M)
  }

  // Optional re-implementation of foreach, 
  // to make it more efficient.
  override def foreach[U](f: Base => U): Unit = {
    var i = 0
    var b = 0
    while (i < length) {
      b = if (i % N == 0) groups(i / N) else b >>> S
      f(Base.fromInt(b & M))
      i += 1
    }
  }
}

object RNA {

  private val S = 2 // number of bits in group
  private val M = (1 << S) - 1 // bitmask to isolate a group
  private val N = 32 / S // number of groups in an Int

  def fromSeq(buf: Seq[Base]): RNA = {
    val groups = new Array[Int]((buf.length + N - 1) / N)
    for (i <- 0 until buf.length)
      groups(i / N) |= Base.toInt(buf(i)) << (i % N * S)
    new RNA(groups, buf.length)
  }

  def apply(bases: Base*) = fromSeq(bases)

  def newBuilder: Builder[Base, RNA] =
    new ArrayBuffer mapResult fromSeq

  implicit def canBuildFrom: CanBuildFrom[RNA, Base, RNA] =
    new CanBuildFrom[RNA, Base, RNA] {
      def apply(): Builder[Base, RNA] = newBuilder
      def apply(from: RNA): Builder[Base, RNA] = newBuilder
    }
}

さて、ここに私の問題があります。これを実行すると、すべて問題ありません。

val rna = RNA(A, G, T, U)
println(rna.map(e => e)) // prints RNA(A, G, T, U)

しかし、このコードは RNA をベクターに変換します!

val rna: IndexedSeq[Base] = RNA(A, G, T, U)
println(rna.map(e => e)) // prints Vector(A, G, T, U)

これは問題です。クラスを認識しないクライアント コードは、からへのみマッピングする場合に代わりにRNAクラスを に戻す可能性があるためです。それはなぜですか、それを修正する方法は何ですか?VectorBaseBase

P.-S .: 暫定的な回答を見つけました (以下を参照)。間違っている場合は修正してください。

4

2 に答える 2

3

rna変数の静的型が の場合IndexedSeq[Base]、自動的に挿入されるをコンパニオン オブジェクトでCanBuildFrom定義されたものにすることはできません。RNAこれは、コンパイラがrnaのインスタンスであることを認識していないためですRNA

それで、それはどこから来たのですか?GenericCanBuildFromコンパイラは、オブジェクトで定義されたのインスタンスにフォールバックしIndexedSeqます。GenericCanBuildFroms は、元のコレクションを呼び出すことによってビルダーを生成しgenericBuilder[B]ます。そのジェネリック ビルダーの要件は、任意の型を保持できるジェネリック コレクションを生成できることBです。もちろん、a に渡される関数の戻り値の型はmap()制約されません。

この場合、RNAは のみでIndexedSeq[Base]あり、ジェネリックIndexedSeqではないため、 をオーバーライドgenericBuilder[B]して 固有のビルダーRNAを返すことはできません — 実行時にisまたは何か他のものであるRNAかどうかを確認する必要がありますが、それはできません。BBase

これが、質問で私たちが戻ってくる理由を説明していると思いますVector。どうすれば修正できるかは未解決の問題です…</p>

編集:これを修正するmap()には、サブタイプにマッピングされているかどうかを知る必要がAあります。これを実現するには、コレクション ライブラリを大幅に変更する必要があります。関連する質問「Scala の map() は、同じ型にマッピングするときに異なる動作をする必要がありますか? 」を参照してください。.

于 2011-04-14T08:46:37.163 に答える
1

RNAよりも弱い型に静的に型付けするのは良い考えではないと思う理由について。それは実際にはコメントであるべきです (それはより意見ですが、それは読みにくいからです)。あなたのコメントから私のコメントへ:

なぜだめですか?IndexedSeq[Base] のサブクラスとして、RNA は、Liskov 置換原理に従って、IndexedSeq[Base] が行うすべてのことを行うことができます。場合によっては、それが IndexedSeq であるということだけを知っていても、フィルター、マップ、およびフレンドが同じ特定の実装を維持することを期待していることがあります。実際、フィルターはそれを行いますが、マップは行いません

filterこれは、コンパイラが静的に保証できるためです。特定のコレクションの要素を保持すると、同じ型のコレクションになります。map渡される関数によって異なります。

私のポイントは、型を明示的に指定し、それが提供できる以上のものを期待するという行為についてです。RNA コレクションのユーザーとして、効率的なメモリ表現など、このコレクションの特定のプロパティに依存するコードを書くことがあります。

val rna: IndexedSeq[Base]それで、私がそれで述べているrna仮定しましょうIndexedSeq。数行後doSomething(rna)、効率的なメモリ表現を期待するメソッドを呼び出します。そのための最良のシグネチャは何でしょうか? def doSomething[T](rna: IndexedSeq[Base]): Tまたはdef doSomething[T](rna: RNA): T

後者であるべきだと思います。しかし、その場合、rnaは静的にRNAオブジェクトではないため、コードはコンパイルされません。メソッド シグネチャが前者である場合、本質的には、メモリ表現効率は気にしないということです。したがって、より弱い型を明示的に指定し、より強い動作を期待するという行為は矛盾していると思います。これは、あなたの例で行うことです。

今、私がしたとしてもそれがわかります:

val rna = RNA(A, G, T, U)
val rna2 = doSomething(rna)

他の誰かが書いた場所:

def doSomething[U](seq: IndexedSeq[U]) = seq.map(identity)

私はオブジェクトにrna2なりたいのですが、それは起こりません...これは、呼び出し元がより具体的な型を取得したい場合RNA、他の誰かが を取るメソッドを書く必要があることを意味します:CanBuildFrom

def doSomething[U, To](seq: IndexedSeq[U])
   (implicit cbf: CanBuildFrom[IndexedSeq[U], U, To]) = seq.map(identity)(cbf)

次に、次のように呼び出すことができます。val rna2: RNA = doSomething(rna)(collection.breakOut)

于 2011-04-14T13:09:03.143 に答える