如何确保在map()期间保留自定义Scala集合的动态类型?
|
我阅读了有关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转换为Vector!
val rna: IndexedSeq[Base] = RNA(A, G, T, U)
println(rna.map(e => e)) // prints Vector(A, G, T, U)
这是一个问题,因为不知道RNA
类的客户端代码可能会将其转换回Vector
,而不是仅从Base
映射到Base
时。为什么会这样,有什么解决方法?
附言:我找到了一个初步的答案(见下文),如果我错了,请纠正我。
没有找到相关结果
已邀请:
2 个回复
抚驰
变量的静态类型为
,则自动插入的
不能是
伴随对象中定义的那个,因为编译器不应该知道
是
的实例。 那么它是从哪里来的呢?编译器将回退到
的实例,该实例在
对象中定义。
通过在原始集合上调用
来生成其生成器,并且对该通用生成器的要求是它可以生成可以容纳任何类型
的泛型集合-当然,传递给
的函数的返回类型不受限制。 在这种情况下,
只是
,而不是通用的
,因此不可能覆盖
中的
以返回特定于
的构建器—我们将不得不在运行时检查
是
还是其他东西,但是我们不能那样做。 我认为这可以解释为什么我们要退还5英镑。至于我们如何解决它,这是一个悬而未决的问题…… 编辑:修复此问题需要
知道它是否映射到
的子类型。为此,需要对馆藏库进行重大更改。查看相关问题Scala的map()在映射到相同类型时是否应该表现出不同?
梦砍废么
这样做是因为编译器可以静态地保证它。如果保留特定集合中的元素,则最终会得到相同类型的集合。
不能保证,这取决于传递的功能。 我的意思更多是关于明确指定类型并期望超出其传递范围的行为。作为RNA收集的用户,我可能编写取决于该收集的某些属性(例如有效的内存表示形式)的代码。 因此,假设我在ѭ33中声明
只是an15ѭ。几行之后,我调用了方法“ 36”,该方法期望有效的内存表示形式,对此最好的签名是什么?
还是
? 我认为应该是后者。但是如果真是这样,那么代码将不会编译,因为
并不是静态的
对象。如果方法签名应该是前者,那么实质上我是说我不在乎内存表示效率。因此,我认为明确指定弱类型但期望强行为的行为是矛盾的。您在示例中所做的是。 现在我确实看到了,即使我这样做了:
别人写的地方:
我想让
成为
对象,但是那不会发生...这意味着如果希望调用者获得更具体的类型,那么其他人应该编写一个采用
的方法:
那我可以打电话给: