идиоматическое "получить или обновить" для immutable.Map? - PullRequest
17 голосов
/ 08 декабря 2010

Каков идиоматический способ getOrElseUpdate для экземпляров immutable.Map? Я использую фрагмент ниже, но он кажется многословным и неэффективным

var map = Map[Key, Value]()

def foo(key: Key) = {
  val value = map.getOrElse(key, new Value)
  map += key -> value
  value
}

Ответы [ 4 ]

13 голосов
/ 30 апреля 2011

Я бы, вероятно, реализовал метод getOrElseUpdated, например, такой:

def getOrElseUpdated[K, V](m: Map[K, V], key: K, op: => V): (Map[K, V], V) =
  m.get(key) match {
    case Some(value) => (m, value)
    case None => val newval = op; (m.updated(key, newval), newval)
  }

, который либо возвращает исходную карту, если m имеет отображение для key, либо другую карту с отображением key -> opдобавлено.Определение этого метода аналогично getOrElseUpdate из mutable.Map.

9 голосов
/ 08 декабря 2010

Позвольте мне обобщить вашу проблему:

  • Вы хотите вызвать метод для неизменной структуры данных
  • Вы хотите, чтобы он возвратил некоторое значение и переназначил var
  • Поскольку структура данных неизменна, вам необходимо
    • возвращает новую неизменяемую структуру данных или
    • выполнить присваивание внутри метода, используя прилагаемое замыкание

Итак, ваша подпись должна выглядеть как

def getOrElseUpdate(key: K): Tuple2[V, Map[K,V]]
//... use it like
val (v, m2) = getOrElseUpdate(k)
map = m2

или

def getOrElseUpdate(key: K, setter: (Map[K,V]) => Unit): V
//... use it like
val v = getOrElseUpdate(k, map = _)

Если вы можете жить с одним из этих решений, вы можете добавить свою собственную версию с неявным преобразованием, но, судя по одним только сигнатурам, я не думаю, что что-то из этого находится в стандартной библиотеке.

8 голосов
/ 08 декабря 2010

Нет такого способа - мутация карты (обновление), когда вы получаете значение карты, является побочным эффектом (который противоречит неизменности / функциональному стилю программирования).

Если вы хотите создать новую неизменяемую карту со значением по умолчанию, если другое значение для указанного ключа не существует, вы можете сделать следующее:

map + (key -> map.getOrElse(key, new Value)) 
2 голосов
/ 08 декабря 2010

Почему бы не использовать withDefault или withDefaultValue, если у вас есть неизменяемая карта?

...