Scala: Почему mapValues ​​создает представление и существуют ли какие-либо стабильные альтернативы?

Сейчас я с удивлением узнаю, что mapValues создает представление. Следствие показано в следующем примере:

case class thing(id: Int)
val rand = new java.util.Random
val distribution = Map(thing(0) -> 0.5, thing(1) -> 0.5)
val perturbed = distribution mapValues { _ + 0.1 * rand.nextGaussian }
val sumProbs = perturbed.map{_._2}.sum
val newDistribution = perturbed mapValues { _ / sumProbs }

Идея состоит в том, что у меня есть распределение, которое возмущается некоторой случайностью, а затем я перенормирую его. Код на самом деле не работает в своем первоначальном намерении: поскольку mapValues создает view, _ + 0.1 * rand.nextGaussian всегда переоценивается всякий раз, когда используется perturbed.

Теперь я делаю что-то вроде distribution map { case (s, p) => (s, p + 0.1 * rand.nextGaussian) }, но это немного немного многословно. Поэтому цель этого вопроса:

  • Напомните людям, которые не знают об этом факте.
  • Посмотрите, почему они делают вывод mapValues view s.
  • Существует ли альтернативный метод, который создает конкретный Map.
  • Существуют ли какие-либо другие широко используемые методы сбора, которые имеют эту ловушку.

Спасибо.

Ответ 1

Там билет об этом, SI-4776 (по YT).

Конец, который вводит его, имеет следующее:

Следуя предложению jrudolph, сделали filterKeys и mapValuesпреобразовывать абстрактные карты и дублировать функциональность для неизменяемых карты. Перемещено transform и filterNot из неизменяемых в общие карты. Обзор phaller.

Я не смог найти оригинальное предложение jrudolph, но я предполагаю, что это было сделано, чтобы сделать mapValues более эффективным. Задайте вопрос, который может стать неожиданностью, но mapValues более эффективен, если вы вряд ли будете перебирать значения более одного раза.

Как работа, можно сделать mapValues(...).view.force для создания нового Map.

Ответ 2

В документе scala doc говорится:

отображение карты, которое отображает каждый key этого отображения на f(this(key)). Полученная карта обертывает исходную карту без копирования каких-либо элементов.

Так что этого следовало ожидать, но это меня очень пугает, мне придется пересмотреть кучу кода завтра. Я не ожидал такого поведения: - (

Просто обходной путь:

Вы можете вызвать toSeq, чтобы получить копию, и если вам нужно вернуть ее обратно на карту toMap, но это ненужные объекты создания и иметь представление о влиянии на использование map

Можно относительно легко писать, a mapValues, который не создает представление, я сделаю это завтра и отправлю код здесь, если никто не сделает это передо мной;)

EDIT:

Я нашел простой способ "форсировать" представление, использовать ".map(identity)" после mapValues ​​(поэтому нет необходимости в реализации определенной функции):

scala> val xs = Map("a" -> 1, "b" -> 2)
xs: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1, b -> 2)

scala> val ys = xs.mapValues(_ + Random.nextInt).map(identity)
ys: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1315230132, b -> 1614948101)

scala> ys
res7: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1315230132, b -> 1614948101)

Стыдно, что возвращаемый тип не является на самом деле видом! иначе можно было бы назвать "силой"...