2 つのリストa
とがある場合b
、これら 2 つ (順序は関係ありません) を効率的な方法で新しいリストに連結するにはどうすればよいでしょうか?
a ::: b
とa ++ b
が効率的であるかどうか、Scala API からはわかりませんでした。多分私は何かを逃した。
2 つのリストa
とがある場合b
、これら 2 つ (順序は関係ありません) を効率的な方法で新しいリストに連結するにはどうすればよいでしょうか?
a ::: b
とa ++ b
が効率的であるかどうか、Scala API からはわかりませんでした。多分私は何かを逃した。
Scala 2.9では、:::
(リストに追加)のコードは次のとおりです。
def :::[B >: A](prefix: List[B]): List[B] =
if (isEmpty) prefix
else (new ListBuffer[B] ++= prefix).prependToList(this)
一方、++
より一般的です。これは、パラメーターを受け取るためです。つまり、 :CanBuildFrom
とは異なるコレクションタイプを返すことができます。List
override def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = {
val b = bf(this)
if (b.isInstanceOf[ListBuffer[_]]) (this ::: that.seq.toList).asInstanceOf[That]
else super.++(that)
}
したがって、リターンタイプがList
、の場合、2つは同じように機能します。
これListBuffer
は、ミューティングビルダーとして使用できるという点で巧妙なメカニズムですが、最終的にはtoList
メソッドによって「消費」されます。つまり、最初に(例では)の(new ListBuffer[B] ++= prefix).prependToList(this)
すべての要素を順番に追加し、O(| a |)時間を取ります。次に、を呼び出します。これは定数時間の操作です(レシーバー、または、を分解する必要はありません)。したがって、全体の時間はO(| a |)です。prefix
a
prependToList
b
一方、pstが指摘したように、次のようになりますreverse_:::
。
def reverse_:::[B >: A](prefix: List[B]): List[B] = {
var these: List[B] = this
var pres = prefix
while (!pres.isEmpty) {
these = pres.head :: these
pres = pres.tail
}
these
}
したがって、を使用するとa reverse_::: b
、これもO(| a |)を使用するため、他の2つの方法よりも効率的ではありません(ただし、リストサイズが小さい場合は、中間ListBuffer
作成のオーバーヘッドを節約できます)。
つまり、との相対的なサイズについて知っている場合は、プレフィックスが2つのリストのうち小さい方であることを確認する必要があります。あなたがその知識を持っていない場合、あなたができることは何もありません。なぜなら、aの操作はO(N)を取ります:)a
b
size
List
一方、将来のScalaバージョンでは、このScalaDaysトークVector
で示されているように、連結アルゴリズムが改善される可能性があります。O(log N)時間でタスクを解決することを約束します。