Ответ 1
Вы наткнулись на сложную функцию реализации Scala Map
. Улов, который вам не хватает, состоит в том, что mapValues
фактически не возвращает новый Map
: он возвращает view
для Map
. Другими словами, он обертывает вашу исходную карту таким образом, что всякий раз, когда вы обращаетесь к значению, он вычисляет .toUpperCase
, прежде чем возвращать вам значение.
Поверхность этого поведения заключается в том, что Scala не будет вычислять функцию для значений, которые не доступны, и она не будет тратить время на копирование всех данных в новый Map
. Недостатком является то, что функция пересчитывается каждый раз, когда это значение получает доступ. Таким образом, вы можете сделать дополнительные вычисления, если вы будете получать одинаковые значения много раз.
Итак, почему SortedMap
не возвращает a SortedMap
? Потому что он фактически возвращает Map
-wrapper. Основополагающий Map
, а затем тот, который завернут, по-прежнему является SortedMap
, поэтому, если вы должны были перебирать, он все равно будет отсортирован в порядке. Мы с тобой это знаем, но у контролера нет. Кажется, что они могли написать его таким образом, что он все еще сохраняет черту SortedMap
, но они этого не сделали.
В коде вы можете видеть, что он не возвращает SortedMap
, но что итерационное поведение все еще будет сортироваться:
// from MapLike
override def mapValues[C](f: B => C): Map[A, C] = new DefaultMap[A, C] {
def iterator = for ((k, v) <- self.iterator) yield (k, f(v))
...
Решение вашей проблемы такое же, как решение проблемы с просмотром: используйте .map{ case (k,v) => (k,f(v)) }
, как вы упомянули в своем вопросе.
Если вы действительно хотите использовать этот метод удобства, вы можете делать то, что я делаю, и написать вам, лучше, версию mapValues
:
class EnrichedWithMapVals[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]) {
/**
* In a collection of pairs, map a function over the second item of each
* pair. Ensures that the map is computed at call-time, and not returned
* as a view as 'Map.mapValues' would do.
*
* @param f function to map over the second item of each pair
* @return a collection of pairs
*/
def mapVals[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = {
val b = bf(self.asInstanceOf[Repr])
b.sizeHint(self.size)
for ((k, v) <- self) b += k -> f(v)
b.result
}
}
implicit def enrichWithMapVals[T, U, Repr <: GenTraversable[(T, U)]](self: GenTraversableLike[(T, U), Repr]): EnrichedWithMapVals[T, U, Repr] =
new EnrichedWithMapVals(self)
Теперь, когда вы вызываете mapVals
на SortedMap
, вы возвращаете не-просмотр SortedMap
:
scala> val m3 = m1.mapVals(_ + 1)
m3: SortedMap[String,Int] = Map(aardvark -> 2, cow -> 6, dog -> 10)
Он фактически работает с любым набором пар, а не только с реализациями Map
:
scala> List(('a,1),('b,2),('c,3)).mapVals(_+1)
res8: List[(Symbol, Int)] = List(('a,2), ('b,3), ('c,4))