如何确保在map()期间保留自定义Scala集合的动态类型?
我读了非常有趣的
article on the architecture of the Scala 2.8 collections,我一直在试验它.首先,我简单地复制了最好的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,RNA] { def apply(): Builder[Base,RNA] = newBuilder def apply(from: RNA): Builder[Base,RNA] = newBuilder } } 现在,这是我的问题.如果我运行这个,一切都很好: val rna = RNA(A,U) println(rna.map(e => e)) // prints RNA(A,U) 但是这段代码将RNA转换为Vector! val rna: IndexedSeq[Base] = RNA(A,U) println(rna.map(e => e)) // prints Vector(A,U) 这是一个问题,因为不知道RNA类的客户端代码可能会在仅从Base映射到Base时将其转换回Vector.为什么会这样,有什么方法可以解决它? P.-S.:我找到了一个试探性的答案(见下文),如果我错了,请纠正我. 解决方法
如果rna变量的静态类型是IndexedSeq [Base],则自动插入的CanBuildFrom不能是RNA伴随对象中定义的那个,因为编译器不应该知道rna是RNA的实例.
那么它来自哪里?编译器依赖于GenericCanBuildFrom的实例,该实例是IndexedSeq对象中定义的实例. GenericCanBuildFroms通过在原始集合上调用genericBuilder [B]来生成它们的构建器,并且该泛型构建器的要求是它可以生成可以包含任何类型B的泛型集合 – 当然,传递给映射的函数的返回类型()不受约束. 在这种情况下,RNA只是一个IndexedSeq [Base]而不是一般的IndexedSeq,因此不可能在RNA中覆盖genericBuilder [B]以返回特定于RNA的构建器 – 我们必须在运行时检查B是否为Base或别的什么,但我们做不到. 我认为这解释了为什么,在这个问题上,我们得到了一个Vector.至于我们如何解决它,这是一个悬而未决的问题…… 编辑:修复此问题需要map()知道它是否映射到A的子类型.为此,需要对集合库进行重大更改.请参阅相关问题Should Scala’s map() behave differently when mapping to the same type?. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |