通用数据结构的默认值

时间:2009-12-04 18:11:33

标签: 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)

如何在班级中包含此默认值?我想得到的行为是:

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'

由于

4 个答案:

答案 0 :(得分:7)

您可以将隐式参数作为第二个参数添加到构造函数中:

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"

并为您关注的类型提供隐含。这也允许调用者通过在:

中传递他们自己的默认值来提供他们自己的默认值
val sparseWithCustomDefault = new SparseVector[String](10) (() => "dwins rules!");

答案 1 :(得分:7)

您可以利用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 }
}

现在代码如下所示:

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

我可能会对这个课程做一些改进:

  • 使用`toString`方法以合理的方式打印集合
  • 提供一个伴随对象,如果需要,可以更改向量的默认值(请参阅下面的代码)。
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
  }
}

现在可行:

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

编辑:我编写的代码适用于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行为不应该是可行的,但不幸的是,它是。做一些more research into the problem似乎由于类型擦除,字段default 总是被初始化为null。之后......奇怪随之而来。请参阅Mitch's post进行讨论,并查看一些重现问题的熊骨代码。

为了使代码正常运行,我尝试过但失败了:

  • null.asInstanceOf[T] - 不,Java没有具体化的泛型。这仍然会产生null
  • @specialised - 不,似乎即使编译器为基元生成专门的代码,你仍然会得到空行为
  • 将结果投射到AnyVal,该null不应为null。不。仍然是null

从概念上讲,我的解决方案应该有效。但这并不是因为我在Scala Trac中有reported的非常奇怪的行为。

另请参阅this blog post,了解AnyVal能{{1}}的详细讨论。

- Flaviu Cipcigan

答案 2 :(得分:4)

您可以使用清单获取与Array相同的默认值,这样就无需提供自己的隐含。再次从David Winslow借用其余代码,

class SparseVector[T](size: Int)(implicit manifest: Manifest[T]) {
    private val default = manifest.newArray(1)(0)
    private var storage = scala.collection.mutable.Map[Int, T]()
    def apply(i: Int) = storage.getOrElse(i, default)
    def update(i: Int, v: T) = storage.update(i, v)
}

然后就是,

val v = new SparseVector[Int](100)
println( v(12) ) // prints '0'

答案 3 :(得分:0)

重新使用David的SparseVector类,您可以使用以下内容:

class SparseVector[T](size: Int, default: T = 0) {
  private var storage = scala.collection.mutable.Map[Int, T]()
  def apply(i: Int) = storage.getOrElse(i, default)
  def update(i: Int, v: T) = storage.update(i, v)
}

object SparseVector {
  implicit def svInt2String(i: Int) = "default"
  implicit def svInt2Boolean(i: Int = false
}

你需要导入implicits,这是一种耻辱,但这会给你: -

import SparseVector._    

val v = new SparseVector[Int](100)
println( v(12) ) // prints '0'
val w = new SparseVector[Double](100)
println( w(12) ) // prints '0.0'
val x = new SparseVector[Boolean](100)
println( x(85) ) // prints 'false'
val y = new SparseVector[String](100)
println( y(85) ) // prints 'default'