web-dev-qa-db-ja.com

リストの先頭に追加する場合の `::`と `+:`の違いは何ですか?)

Listには、要素を(不変の)リストに追加するために指定される2つのメソッドがあります。

  • +:Seq.+:の実装)、および
  • ::Listでのみ定義)

+:技術的には、より一般的なタイプ署名があります—

def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That
def ::[B >: A](x: B): List[B]

—しかし、暗黙の無視は、docメッセージによれば、ThatList[B]である必要があるだけであるため、署名は同等です。

List.+:List.::の違いは何ですか?実際に同じ場合、+:は具体的な実装Listに依存しないようにすることをお勧めします。しかし、なぜ別のパブリックメソッドが定義され、クライアントコードはいつそれを呼び出すのでしょうか?

編集

パターンマッチングには::の抽出機能もありますが、これらの特定のメソッドについては疑問に思っています。

参照: Scala list concatenation、::: vs ++

43

両方の方法の違いを判断する最良の方法は、ソースコードを調べることです。

source of _::_:

_def ::[B >: A] (x: B): List[B] =
  new scala.collection.immutable.::(x, this)
_

source of _+:_:

_override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That = bf match {
  case _: List.GenericCanBuildFrom[_] => (elem :: this).asInstanceOf[That]
  case _ => super.+:(elem)(bf)
}
_

ご覧のとおり、Listの場合、両方のメソッドが同じことを行います(コンパイラはCanBuildFrom引数に List.canBuildFrom を選択します)。

それで、どの方法を使用するのですか?通常、実装(_+:_)よりもインターフェイス(_::_)を選択しますが、Listは関数型言語の一般的なデータ構造であるため、独自のメソッドが広く使用されています。多くのアルゴリズムは、Listの動作方法を構築します。たとえば、単一の要素をListの前に追加したり、便利なheadまたはtailメソッドを呼び出す多くのメソッドがあります。これらの操作はすべてO(1)であるためです。 。したがって、ローカルでList(単一のメソッドまたはクラス内)で作業する場合、List固有のメソッドを選択しても問題はありません。ただし、クラス間で通信したい場合、つまりいくつかのインターフェイスを作成したい場合は、より一般的なSeqインターフェイスを選択する必要があります。

36
kiritsuku

+:は、結果の型を呼び出し元のオブジェクトの型と異なるものにすることができるため、より一般的です。例えば:

scala> Range(1,4).+:(0)
res7: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 2, 3)
11
Kim Stebel