11

私はScalaが初めてです。私は自分のコードでSortedMapを使用しており、 mapValuesを使用して、値に何らかの変換を加えた新しいマップを作成したいと考えていました。

mapValues関数は、新しい SortedMap を返す代わりに、新しいMap返すので、これをSortedMapに変換する必要があります。

例えば

val my_map = SortedMap(1 -> "one", 0 -> "zero", 2 -> "two")
val new_map = my_map.mapValues(name => name.toUpperCase)
// returns scala.collection.immutable.Map[Int,java.lang.String] = Map(0 -> ZERO, 1 -> ONE, 2 -> TWO)
val sorted_new_map = SortedMap(new_map.toArray:_ *)

これは非効率に見えます - 最後の変換はおそらくキーを再度ソートするか、少なくともソートされていることを確認します。

キーと値の両方で動作する法線マップ関数を使用して、意図的に変換関数のキーを変更しないようにすることができます。Mapの実装はおそらく、変換によってキーの順序が変更される可能性があると想定しているため (: の場合のように) 、これも非効率に見えますmy_map.map(tup => (-tup._1, tup._2)。したがって、おそらくそれらも「再ソート」します。

MapSortedMapの内部実装に精通している人はいますか?私の仮定が正しいかどうか教えてもらえますか? コンパイラは、キーが並べ替えられていないことを自動的に認識できますか? mapValuesがSortedMapを返してはならない内部的な理由はありますか? キーの順序を失うことなくマップの値を変換するより良い方法はありますか?

ありがとう

4

1 に答える 1

16

Scala のMap実装のトリッキーな機能に出くわしました。あなたが見逃しているキャッチは、mapValues実際には new を返さないことですMap: aviewの a を返しますMap。つまり、値にアクセスするたびに.toUpperCase値を返す前に計算するように、元のマップをラップします。

この動作の利点は、Scala がアクセスされていない値の関数を計算せず、すべてのデータを new にコピーするのに時間を費やさないことMapです。欠点は、その値がアクセスされるたびに関数が再計算されることです。そのため、同じ値に何度もアクセスすると、余分な計算が必要になる場合があります。

では、なぜSortedMapa を返さないの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)

mapValsaを呼び出すと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))
于 2012-09-26T21:08:36.703 に答える