7

Scala の SortedMap[A,B] を使っていると、許可されていない奇妙さに遭遇しました。SortedMap[A,B] "a" への参照が Map[A,B] 型であると宣言すると、"a" に対するマップ操作によって、ソートされていないマップの実装が生成されます。

例:

import scala.collection.immutable._

object Test extends App {
    val a: Map[String, String] = SortedMap[String, String]("a" -> "s", "b" -> "t", "c" -> "u", "d" -> "v", "e" -> "w", "f" -> "x")
    println(a.getClass+": "+a)

    val b = a map {x => x}  // identity
    println(b.getClass+": "+b)
}

上記の出力は次のとおりです。

class scala.collection.immutable.TreeMap: Map(a -> s, b -> t, c -> u, d -> v, e -> w, f -> x)
class scala.collection.immutable.HashMap$ HashTrieMap: Map(e -> w, f -> x, a -> s, b -> t, c -> u, d -> v)

恒等変換の前後のキーと値のペアの順序は同じではありません。

奇妙なことに、「a」から型宣言を削除すると、この問題が解消されます。これはおもちゃの例では問題ありませんが、SortedMap[A,B] を Map[A,B] パラメーターを期待するメソッドに渡すために使用できなくなります。

一般に、「マップ」や「フィルター」などの高次関数は、それらが適用されるコレクションの基本的なプロパティを変更しないと予想されます。

「マップ」がこのように動作する理由を知っている人はいますか?

4

4 に答える 4

2

このmapメソッドは、ほとんどのコレクション メソッドと同様に、 のために特別に定義されていませんSortedMap。これは上位クラス ( TraversableLike ) で定義され、「ビルダー」を使用してマップされた結果を正しい戻り値の型に変換します。

では、「正しい」戻り値の型をどのように決定するのでしょうか。ええと、最初の戻り値の型を返そうとします。Scala に a があることを伝えてMap[String,String]を要求するとmap、ビルダーは返される型をどのように「構築」するかを考え出さなければなりません。入力が a であると Scala に伝えたのでMap[String,String]、ビルダーはあなたのために a をビルドすることを決定しMap[String,String]ます。ビルダーは、あなたが を望んでいることを知らないSortedMapので、それを与えません。

theMap[String,String]型注釈を省略しても機能する理由は、Scala が の型がaisであると推測するためですSortedMap[String,String]。したがって、 を呼び出すときはmap、 でそれを呼び出すことにSortedMapなり、ビルダーSortedMapは返すために を構築することを認識します。

メソッドが「基本的なプロパティ」を変更すべきではないというあなたの主張に関しては、間違った角度から見ていると思います。メソッドは、指定した型に準拠するオブジェクトを常に返します。基礎となる実装ではなく、ビルダーの動作を定義するのは型です。そのように考えると、メソッドがどのように振る舞うべきかについての契約を形成するのは型です。

なぜこれが必要なのですか?

なぜこれが好ましい動作なのですか? 具体例を見てみましょう。私たちが持っているとしましょうSortedMap[Int,String]

val sortedMap = SortedMap[Int, String](1 -> "s", 2 -> "t", 3 -> "u", 4 -> "v")

キーをmap変更する関数を使用すると、キーが衝突したときに要素を失うリスクがあります。

scala> sortedMap.map { case (k, v) => (k / 2, v) }
res3: SortedMap[Int,String] = Map(0 -> s, 1 -> u, 2 -> v)

でもまあ、いいんです。Map結局のところ、それが であることはわかっているのでMap、その動作を期待する必要があります。

Iterableここで、ペアのを受け入れる関数があるとします。

def f(iterable: Iterable[(Int, String)]) = 
  iterable.map { case (k, v) => (k / 2, v) }

この関数は s とは何の関係もないので、この関数の結果が入力よりも要素が少ない場合は非常にMap驚くでしょう。結局、onは各要素のマップされたバージョンを生成する必要があります。しかし、aペアの1 つなので、この関数に渡すことができます。では、これを行うと Scala では何が起こるでしょうか?mapIterableMap Iterable

scala> f(sortedMap)
res4: Iterable[(Int, String)] = List((0,s), (1,t), (1,u), (2,v))

それを見て!失われた要素はありません!言い換えれば、Scala は、 がどのように機能するかについての私たちの期待に反して、私たちを驚かせることはありませmapIterableSortedMap入力が であるという事実に基づいてビルダーが代わりに を生成しようとした場合、SortedMap関数fは驚くべき結果をもたらし、これは悪いことになります。

したがって、この話の教訓は次のとおりです。型を使用して、コレクション フレームワークにデータの処理方法を伝えます。マップがソートされていることをコードで想定できるようにする場合は、次のように入力する必要がありますSortedMap

于 2012-09-27T20:20:29.947 に答える
1

dyross が指摘しているように、対象の型に基づいて (CanBuildFrom を介して) 選択されるのは Builder であり、map操作から取得するコレクションのクラスを決定します。これはあなたが望んでいた動作ではないかもしれませんが、たとえば、ターゲットの種類を選択することができます:

val b: SortedMap[String, String] = a.map(x => x)(collection.breakOut) 

(型がコンテキストによって決定されるbreakOutジェネリックCanBuildFrom、つまり型注釈を提供します。)

したがって、任意の種類の Map または Traversable を受け入れることができるいくつかの型パラメーターを追加できます (この質問を参照してください)。これにより、正しい型情報を保持しながらメソッドでマップ操作を実行できますが、ご覧のとおり、簡単ではありません。 .

コレクション自体をメソッドに送信するのではなく、コレクションの などのメソッドmapを使用してコレクションに適用する関数を定義する方が、はるかに簡単なアプローチだと思います。flatMap

つまり、代わりに

def f[Complex type parameters](xs: ...)(complex implicits) = ...
val result = f(xs)

行う

val f: X => Y = ...
val results = xs map f
于 2012-09-27T22:53:07.193 に答える
1

の署名mapは次のとおりです。

def map[B, That](f: ((A, B)) ⇒ B)(implicit bf: CanBuildFrom[Map[A, B], B, That]): That

bf結果のコレクションを構築するために、暗黙的なパラメーターが使用されます。したがって、あなたの例では、のタイプはaisMap[String, String]であるため、のタイプは次のbfとおりです。

val cbf = implicitly[CanBuildFrom[Map[String, String], (String, String), Map[String, String]]]

Map[String, String]のプロパティを持たない を構築するだけSortedMapです。見る:

cbf() ++= List("b" -> "c", "e" -> "g", "a" -> "b") result

詳細については、次の優れた記事を参照してください: http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html

于 2012-09-27T21:39:34.663 に答える
0

要するに:a型であると明示的に宣言した場合、Scala コレクション フレームワークは、 andMapなどの高階関数に対して、それらが適用されるコレクションの基本的なプロパティを変更しないように非常に懸命に努力します。あなたはそれが欲しいと明示的に言いました。mapfilterMap

于 2012-09-27T23:46:38.287 に答える