LINUX.ORG.RU

Mutable map with default value in scala

 


0

1

Как в scala создать mutable.Map, чтобы при обращении создавалось значение по умолчанию?

Нужен аналог вот такого:

from collections import defaultdict

d = defaultdict(list)
d[0].extend((1, 2, 3))
d[2].extend((4, 5, 6))

for k, v in d.items():
  print str(k) + ': ' + str(v)
или такого:
#include <map>
#include <vector>
#include <iostream>

int main() {
  std::map<int, std::vector<int>> m;
  m[0] = {1, 2, 3};
  m[2] = {4, 5, 6};
  for (auto& v : m) {
    std::cout << v.first << ": ";
    for (auto i : v.second)
      std::cout << i << ", ";
    std::cout << std::endl;
  }
}

Попытка применить метод withDefault ничего не дает:

import scala.collection._

object TestMap extends App {
  type Values = mutable.ArrayBuffer[Int]
  val m = new mutable.HashMap[Int, Values].withDefault(i => new Values())
  m(0).append(1, 2, 3)
  m(2).append(4, 5, 6)
  println(m)
}

Как это делается в scala?

★★★
Ответ на: комментарий от maxcom

Не похоже. Меня интересует как общем случае избавиться от вот такого кода:

import scala.collection._

object TestMap extends App {
  type Values = mutable.ArrayBuffer[Int]
  val m = new mutable.HashMap[Int, Values]

  if (!m.contains(0))
    m(0) = new Values()
  m(0).append(1, 2, 3)

  if (!m.contains(2))
    m(2) = new Values()
  m(2).append(4, 5, 6)

  println(m)
}

Нужно, чтобы значения сами создавались при первом обращении как в python и C++ вариантах.

kamre ★★★
() автор топика
Ответ на: комментарий от Gvidon

Не очень понял как это применить... Можно пример?

Значение для ключа может уже быть в Map, тогда нужно его модифицировать. А если его нет, то должно создаться default значение и уже оно модифицироваться.

kamre ★★★
() автор топика
Ответ на: комментарий от kamre

Я сам новичок в скале, но насколько я понял, совсем так же, как в плюсах или питоне не получится. Значение по-умолчанию позволяет получать это самое значение, если ключ не найден, то сама пара «ключ-значение» при этом в мапу не добавляется. Оператор += позволяет добавить новое значение или перетереть старое, если таковое уже существует. Можно попробовать их объединить, попробуй что-то вроде:

m += m(key).append(blah)

Здесь ты сначала получаешь существующее значение или значение по-умолчанию, модифицируешь его и кладёшь в нужное место. Под рукой сейчас скалы нет, проверить не могу, но мне кажется, что должно сработать.

Gvidon ★★★★
()
Последнее исправление: Gvidon (всего исправлений: 2)
Ответ на: комментарий от Gvidon

Конечно же, должно быть

m += key -> m(key).append(blah)

Gvidon ★★★★
()

m(0).append(1, 2, 3)

упражнение для начинающих ношаться со стандартной библиотекой скалы: m(0).eq(m(0)) //false

а вот теперь возьми и сделай m.put(0, mutable.ArrayBuffer(1,2,3))

хочешь поменять? m.get(0).map(x=>x.append(4))

RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

ну и еще кусочек m.get(0).get.eq(m.get(0).get) //true. Теперь понял?

RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

хочешь поменять?

Нет, хочу чтобы при отсутствии пары (key -> value) автоматически добавлялась пара (key -> default), и возвращалась ссылка на новый объект default. Именно так это работает для python/C++, и это удобно.

m += key -> m(key).append(blah)

Вот может быть так и сработает, но это же не удобно использовать. Что если key это выражение?

kamre ★★★
() автор топика
Ответ на: комментарий от kamre

Вот может быть так и сработает, но это же не удобно использовать.

не сработает, потому как append озвращает Unit. Да и такую технику нет смысла исползовать, потому как m(key) возвращает копию объекта, то есть все преимущества мутабельности куда-то исчезают.

Нет, хочу чтобы при отсутствии пары (key -> value) автоматически добавлялась пара (key -> default)

m.getOrElseUpdate(0, new Values).append(1,2,3,4)

RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

m.getOrElseUpdate(0, new Values).append(1,2,3,4)

Офигеть удобство, каждый раз такую портянку писать, да еще и новый объект Values создавать? Может быть есть нормальное решение?

kamre ★★★
() автор топика
Ответ на: комментарий от kamre

да еще и новый объект Values создавать

так он в любом случае бы создавался. Хочешь совсем удобности, определи метод, какой тебе нравится

RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

так он в любом случае бы создавался

Не в любом, а только если для ключа еще нет значения.

Хочешь совсем удобности, определи метод, какой тебе нравится

А как «добавить» к scala.collection.mutable.Map такой метод update(...) или apply(...), чтобы работало так как мне нужно? Какие-то implicits нужно использовать?

kamre ★★★
() автор топика
Ответ на: комментарий от kamre

Не в любом, а только если для ключа еще нет значения.

в тм кусочке кода именно такое поведение

getOrElseUpdate(key: A, op: ⇒ B): B

лямбда же вторым параметром.

А как «добавить» к scala.collection.mutable.Map такой метод update(...) или apply(...), чтобы работало так как мне нужно?

а как бы ты в с++ решил эту проблему?

import scala.collection._

trait ConvenienceMap[T, U] extends mutable.Map[T, U]{
  def default:U
  def u(k:T):U = getOrElseUpdate(k, default )
}

object CM extends App {
  type Values = mutable.ArrayBuffer[Int]
  val m = new mutable.HashMap[Int, Values] with ConvenienceMap[Int, Values]{def default:Values = new Values()}
  m.u(0).append(1,2,3,4)
  println(m(0))
}
RedPossum ★★★★★
()
Ответ на: комментарий от RedPossum

А можно да, implicit classes пользовать.

import scala.collection._

object ConvenienceMapHelper{
  implicit class ConvenienceMap[T,U](m:mutable.Map[T, U]){
    def u(k:T):U = m.getOrElseUpdate(k, m.default(k) )
  }
}

object CM extends App {
  import ConvenienceMapHelper._
  type Values = mutable.ArrayBuffer[Int]
  val m = new mutable.HashMap[Int, Values].withDefaultValue(new Values)
  m.u(0).append(1,2,3,4)
  println(m(0))
}
RedPossum ★★★★★
()
Последнее исправление: RedPossum (всего исправлений: 2)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.