views:

319

answers:

4

I would like to write a SparseVector[T] class where T can be a double, an int or a boolean.

The class will not be backed by an array (because I want a sparse data structure) but I have seen that when I build an empty array of an AnyVal type, the elements are initialized to the default value. For instance:

 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)

How can I include this default value in my class ? The behaviour I would like to get is:

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'

Thanks

+5  A: 

You could add an implicit argument as a second parameter to the constructor:

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"

And provide implicits for the types you care about. This also allows callers to provide their own defaults, by passing their own default values in:

val sparseWithCustomDefault = new SparseVector[String](10) (() => "dwins rules!");
David Winslow
Nice suggestion, but I am not comfortable with `implicit` parameter. I am afraid of colliding definitions.
paradigmatic
A: 

Re-using David's SparseVector class, you could use something like this:

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
}

You need to Import the implicits, which is a shame, but this gives you:-

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'
Don Mackenzie
+3  A: 

You can use a manifest to get the same default as for Array, which avoids the need to provide your own implicits. Borrowing the rest of the code again from 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)
}

Then just,

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

etc.

Matt R
Looks like a dirty hack at first, but this solution avoids importing implicits.
paradigmatic
This is manifestly better than my effort, thanks Matt ;@]
Don Mackenzie
+4  A: 
Flaviu Cipcigan
Using 2.8r.19890, this returns null as the default for Booleans (or any of the AnyVals)
Mitch Blevins
@Mitch Hm... that must be a bug then. Logically, an `AnyVal` type should not be `null`. The type of `null` is `Null`, which is a bottom type of `AnyRef`. If you try to assign `null` to an `AnyVal` type you should get a compile error - `val b: Boolean = null` yields a type mismatch (found `Null`, required `Boolean`). Or it might be due to the collection redesign (with which I am not familiar) - I suspect that the `getOrElse` method behaves strangely in that case. Try `var b: Boolean = _` in the Scala REPL. It should default to `false` :).
Flaviu Cipcigan
The behavior that led to my confusion is posted here: http://stackoverflow.com/questions/1853397/scala-default-values-behavior
Mitch Blevins
So if I use `private var default: T = null.asInstanceOf[T]` it will be compatible with scala 2.7 and 2.8 ?
paradigmatic
Unfortunately not :(. See my revised answer for a short discussion.
Flaviu Cipcigan