Вы можете использовать тот факт, что 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. Митч Блевинс указал, что код выдает null
в качестве значения по умолчанию для типов AnyVal
при запуске с Scala 2.8r.19890. Как поясняется в комментариях, это не должно быть возможным, поскольку Null
не является подтипом AnyVal
. . Общая идея должна быть аналогичной при использовании 2.8, так как var b: Boolean = _
все равно должна давать вам значение по умолчанию для Boolean
тип. Использование коллекций для хранения разреженного вектора может быть другим, но, как я сказал в комментарии, я не знаком с редизайном коллекции 2.8.
EDIT2: ... поведение null
не должно быть возможным, но, к сожалению, это так. Если провести еще исследования проблемы , кажется, что из-за стирания типа поле default
всегда инициализируется до null
. И после этого ... странность наступает. См. пост Митча для обсуждения и некоторого медвежьего кода, воспроизводящего проблему.
Вещи, которые я пробовал и потерпел неудачу, чтобы заставить код работать как следует:
null.asInstanceOf[T]
- Нет, у Java нет улучшенных обобщений. Это все еще дает null
@specialised
- нет, похоже, что даже если компилятор генерирует специализированный код для примитивов, вы все равно получаете нулевое поведение
- Приведение результата к
AnyVal
, который не должен быть null
. Нету. Все еще null
.
Так что концептуально мое решение должно работать. Но это не из-за очень странного поведения, о котором я сообщил в Scala Trac.
См. Также это сообщение в блоге для хорошего обсуждения null
способного AnyVal
с.
- Flaviu Cipcigan