2013-02-14 2 views
45

Сейчас я с удивлением узнаю, что mapValues создает вид. Последствие показано в следующем примере:Scala: Почему 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) }, но это всего лишь немного подробный. Таким образом, цель этого вопроса:

  1. Напомните людям, которые не знают об этом факте.
  2. Ищите причины, по которым они делают mapValuesview s.
  3. Существует ли альтернативный метод, который дает бетон Map.
  4. Есть ли другие широко используемые методы сбора, которые имеют эту ловушку.

Спасибо.

+1

OMG есть другое место, в котором я использую 'mapValues', поэтому он так медленно работает ... – Kane

+1

Я тоже был укушен. – ziggystar

+0

Сегодня я тоже вступил в эту ловушку. –

ответ

30

Существует билет об этом, SI-4776 (по YT).

коммит, который вводит его это сказать:

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

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

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

+4

приятно, но все же мне интересно, почему mapValues ​​не возвращает представление напрямую, чтобы сделать его более явным? –

+1

@AloisCochard Да, это хороший момент. Гораздо лучше, если тип возврата - 'view', поэтому мы предупреждены о том, что там происходит ... – Kane

+3

@AloisCochard Как вы можете видеть в билете, это именно то, о чем я просил. Это имело бы дополнительную выгоду от непосредственного использования метода «force». –

10

Ла Скала док сказать:

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

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

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

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

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

EDIT:

Я нашел легкий путь к «силе» мнению, не использовать «.map (идентичность)» после 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) 

Это позор типа возврат не на самом деле вид! в противном случае можно было бы назвать «силой» ...

+1

работает с Scala '2.12.0-M3', мне непонятно, что' map (identity) 'покупает вас:' Map ("a" -> 1, "b" -> 2) .mapValues ​​(_ + Random .nextInt) 'возвращает' scala.collection.immutable.Map [String, Int] = Карта (a -> 1496073565, b -> -1842623900) '. Не могли бы вы рассказать? Я думаю, что потенциальная проблема «mapValues» заключается в том, что она лениво оценивает значения, но я не уверен. Спасибо –

+0

Это безумно удивительно. как карта вида может отображать конкретную карту вместо представления? это выходит за рамки непоследовательности. это полное отсутствие рассмотрения. подтвердите из исходного кода, что эта вещь все еще существует в 2.12. – HuStmpHrrr

Смежные вопросы