par*_*tic 7 generics scala default-value
我想写一个SparseVector[T]类,其中T可以是double,int或boolean.
该类不会被数组支持(因为我想要一个稀疏的数据结构),但我已经看到,当我构建一个AnyVal类型的空数组时,元素被初始化为默认值.例如:
scala> new Array[Int](10)
res0: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> new Array[Boolean](10)
res1: Array[Boolean] = Array(false, false, false, false, false, false, false, false, false, false)
scala> new Array[Double](10)
res2: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
Run Code Online (Sandbox Code Playgroud)
如何在班级中包含此默认值?我想要的行为是:
val v = new SparseVector[Double](100)
println( v(12) ) // should print '0.0'
val w = new SparseVector[Boolean](100)
println( v(85) ) // should print 'false'
Run Code Online (Sandbox Code Playgroud)
谢谢
您可以将隐式参数作为第二个参数添加到构造函数中:
class SparseVector[A](size: Int) (implicit default: () => A) {
private var storage = scala.collection.mutable.Map[Int, A]()
def apply(i: Int) = storage.getOrElse(i, default())
def update(i: Int, v: A) = storage.update(i, v)
}
implicit def strDefault(): String = "default"
Run Code Online (Sandbox Code Playgroud)
并为您关心的类型提供隐含.这也允许调用者通过传递他们自己的默认值来提供他们自己的默认值:
val sparseWithCustomDefault = new SparseVector[String](10) (() => "dwins rules!");
Run Code Online (Sandbox Code Playgroud)
您可以利用Scala已经为您提供获取类型默认值的方法这一事实.当你写var x: Int = _,这个初始化x为0.所有AnyVal类型都相似.所有AnyRef类型都初始化为null.
记住,你可以重写你的稀疏矢量类如下:
class SparseVector[T](val size: Int) {
import scala.collection.mutable.Map
private var default: T = _
private[this] val storage = Map[Int, T]()
def apply(key: Int) =
if(key < size)
storage.getOrElse(key, default)
else
throw new IllegalArgumentException("Index " + key + " out of bounds")
def update(key: Int, value: T) { storage(key) = value }
}
Run Code Online (Sandbox Code Playgroud)
现在代码如下所示按预期工作:
scala> val b = new SparseVector[Boolean](10)
b: SparseVector[Boolean] = SparseVector@cfd22a
scala> b(1)
res20: Boolean = false
scala> b(1) = true
scala> b(1)
res22: Boolean = true
scala> val i = new SparseVector[Int](10)
i: SparseVector[Int] = SparseVector@1813c12
scala> i(1)
res23: Int = 0
scala> i(1) = 10
scala> i(1)
res25: Int = 10
scala> i(10)
java.lang.IllegalArgumentException: Index 10 out of bounds
Run Code Online (Sandbox Code Playgroud)
我可能会对这个课程做一些改进:
object SparseVector {
def apply[T](size: Int) = new SparseVector[T](size)
def apply[T](size: Int, default: T) = {
val result = new SparseVector[T](size)
result.default = default
result
}
}
Run Code Online (Sandbox Code Playgroud)
现在这个工作:
scala> val b = SparseVector[Boolean](10, true)
b: SparseVector[Boolean] = SparseVector@126f29f
scala> b(4)
res28: Boolean = true
scala> val i = SparseVector[Int](10, 42)
i: SparseVector[Int] = SparseVector@b9979b
scala> i(3)
res30: Int = 42
Run Code Online (Sandbox Code Playgroud)
编辑:我写的代码适用于Scala 2.7.6.final.Mitch Blevins指出,当使用Scala 2.8r.19890运行时,代码会产生类型null的默认值AnyVal.正如评论中所解释的那样,这不应该是可能的,因为Null它不是一个子类型AnyVal.如果使用2.8,一般的想法应该是相似的,因为var b: Boolean = _仍然应该给你Boolean类型的默认值.使用集合来存储稀疏向量可能会有所不同,但正如我在评论中所说,我不熟悉2.8集合的重新设计.
EDIT2: ......这种null行为不应该是可能的,但不幸的是,这是行为.对问题做一些更多的研究似乎由于类型擦除,字段default 总是初始化为null.之后......奇怪随之而来.请参阅Mitch的帖子进行讨论,并查看一些重现问题的熊骨代码.
为了使代码正常工作,我尝试过但失败了:
null.asInstanceOf[T] - 不,Java没有具体化的泛型.这仍然产生null@specialised - 不,似乎即使编译器为基元生成专门的代码,你仍然会得到null行为AnyVal,不应该是null.不.还是null.从概念上讲,我的解决方案应该有效.但它并不是由于我在Scala Trac中报告的非常奇怪的行为.
另请参阅此博客文章,以便对有null能力AnyVal的讨论进行讨论.
- Flaviu Cipcigan