RNA メソッドの戻り値型の適応 | 目次 |
以下に RNA1 を使った具体例を示す:
scala> rna1.length | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res2: Int = 5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
scala> rna1.last | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res3: Base = C | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
scala> rna1.take(3) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res4: IndexedSeq[Base] = Vector(A, U, G) |
最初の二つの結果は予想通りかもしれないが、最後の rna1 から先頭の三つの要素を take した結果は予想外だったかもしれない。事実、戻り値は静的型として IndexedSeq[Base] をとり、動的型として Vector をとる。RNA1 値を期待していたかもしれない。しかし、RNA1 クラスのコードで行ったのは RNA1 を IndexedSeq から継承しただけだったので、それは不可能だ。一方 IndexedSeq クラスは IndexedSeq を返す take メソッドを持ち、 IndexedSeq のデフォルトの実装は Vector だ。 上記のやりとりの最後の行に出てくるベクトルはここからきている。
final class RNA2 private ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val groups: Array[Int], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
val length: Int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) extends IndexedSeq[Base] with IndexedSeqLike[Base, RNA2] { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import RNA2._ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
override def newBuilder: Builder[Base, RNA2] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new ArrayBuffer[Base] mapResult fromSeq | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def apply(idx: Int): Base = // 前と同じ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
現状の把握ができた所で、これを解決するために何を変更するべきかというのが次の問題だろう。一つの方法としては、RNA1 クラスの take メソッドをオーバーライドしてしまうというのがある:
def take(count: Int): RNA1 = RNA1.fromSeq(super.take(count)) |
これを実現するために IndexedSeqLike 自身が、正しい種類のビルダを作成する newBuilder 抽象体に基づいている。IndexedSeqLike トレイトの子クラスは自身の種類のコレクションを返すために newBuilder をオーバーライドしなくてはいけない。RNA2 クラスでは newBuilder メソッドは Builder[Base, RNA2] 型のビルダを返す。
このビルダを構築するために、まず自身が Builder[Base, ArrayBuffer] でもある ArrayBuffer が作成される。次に、ArrayBuffer ビルダを mapResult を呼び出すことで RNA2 ビルダに変換する。mapResult メソッドは、ArrayBuffer から RNA2 への変換関数を引数として期待する。渡された関数は、任意の塩基列から RNA2 に変換する RNA2.fromSeq だ(配列バッファは列の一種なので、RNA2.fromSeq を適用できる)。
もし newBuilder を定義しなかったとすると、以下のようなエラーが発生しただろう:
RNA2.scala:5: error: overriding method newBuilder in trait | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TraversableLike of type => scala.collection.mutable.Builder[Base,RNA2]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
method newBuilder in trait GenericTraversableTemplate of type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
=> scala.collection.mutable.Builder[Base,IndexedSeq[Base]] has | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
incompatible type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class RNA2 private (val groups: Array[Int], val length: Int) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
one error found |
込み入ったコレクションライブラリの仕組みを反映して、このエラーメッセージも非常に長く複雑なものになっている。メソッドの由来に関する情報は、この場合は、役に立つというよりは問題の核心から注意を逸らすことになるので無視することにする。残りは、戻り値型が Builder[Base, RNA2] である newBuilder メソッドが定義される必要があるが、戻り値型が Builder[Base,IndexedSeq[Base]] のメソッド newBuilder ということだ。後者は前者をオーバーライドしない。戻り値型が Builder[Base, RNA2] の最初のメソッドは、RNA2 クラスのコードにおいて、IndexedSeqLike の型パラメータに RNA2 が渡されたことによってこの型でインスタンス化された抽象メソッドだ。戻り値型が Builder[Base,IndexedSeq[Base]] の二番目のメソッドは、継承した IndexedSeq クラスによって提供されているものだ。別の言い方をすると、最初の戻り値型の newBuilder 無しでは RNA2 は無効ということだ。
RNA2 クラスのより洗練された実装により、take、drop、filter などのメソッドは期待通り作動する:
scala> val rna2 = RNA2(A, U, G, G, T) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rna2: RNA2 = RNA2(A, U, G, G, T) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
scala> rna2 take 3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res5: RNA2 = RNA2(A, U, G) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
scala> rna2 filter (U !=) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
res6: RNA2 = RNA2(A, G, G, T) |
続いては、map 類の取り扱い
RNA メソッドの戻り値型の適応 | 目次 |