19

toSeq不変のSetコレクションを呼び出すと、 ArrayBuffer.

scala> Set(1,2,3).toSeq // returns Seq[Int] = ArrayBuffer(1, 2, 3)

これは私を驚かせます。Scala が不変のデータ構造を使用することに重点を置いていることを考えると、 mutable の代わりにVectororのような不変のシーケンスが返されることを期待しています。セット要素の返される順序はもちろん未定義である必要がありますが、その順序も変更可能にする意味的な理由はないようです。ListArrayBuffer

一般に、変更可能な結果を​​明示的に要求しない限り、Scala 操作は常に不変の結果を生成することを期待しています。これはずっと私の仮定でしたが、ここでは正しくありません。実際には、予期しない の存在によってステートメントArrayBufferで実行時エラーが発生するという問題をデバッグするのに 1 時間費やしました。match私の修正は に変更Set(...).toSeqすることSet(...).toListでしたが、その時点で特にリストを必要とするアプリケーションが何もないため、これはハックのように感じます。

可変オブジェクトをSet(...).toSeq返すことは Scala の実装の欠陥ですか、それとも私がここで誤解している原則はありますか?

これは Scala 2.9.2 です。

4

2 に答える 2

12

これは、 Seqが immutable.Seq を意味するかどうかに関する最近のスレッドです。

ローランド・クーン:

collection.Seq にミューテーターがないことは、有効な防御策とは言えません!

変更可能な可変引数の例はかなり卑劣です。

最近、

scala> Set(1,2,3)
res0: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> res0.toSeq
res1: Seq[Int] = ArrayBuffer(1, 2, 3)

scala> res0.to[collection.immutable.Seq]
res2: scala.collection.immutable.Seq[Int] = Vector(1, 2, 3)
于 2012-12-04T05:45:32.950 に答える
11

少し奇妙だと思いますが、欠陥ではないと思います。まず、これを考えてみましょう: のコンパイル時の型Set.toSeq

() => Seq[Int]

いいえ

() => ArrayBuffer[Int]

ArrayBufferたまたま、返されたオブジェクトの実行時の型になっています (おそらく、Set.toSeqに追加し、ArrayBuffer変換せずにそれを返すためです)。

したがって、toSeq可変オブジェクトが返されたとしても、実際にそれを変更することはできません (キャストまたはパターン マッチングなしではArrayBuffer-- したがって、本当の「奇妙な」部分は、Scala では任意のクラスでパターン マッチングができることです)。(オブジェクトを保持して変異させないことを信頼する必要がSetありますが、それは公正な仮定だと思います)。

別の見方をすると、可変型は不変型より厳密に具体的です。または、別の言い方をすれば、可変オブジェクトはすべて不変オブジェクトとして扱うこともできます。不変オブジェクトにはゲッターがあり、可変オブジェクトにはゲッターセッターがありますが、それでもゲッターがあります。

もちろん、これは悪用される可能性があります。

val x = new Seq[Int] {
    var n: Int = 0
    def apply(k: Int) = k
    def iterator = {
        n += 1
        (0 to n).iterator
    }
    def length = n
}

x foreach println _
0
1

x foreach println _
0
1
2

しかし、まあ、多くのことができます。

于 2012-12-04T05:18:10.933 に答える