web-dev-qa-db-ja.com

リストへの追加が悪いのはなぜですか?

最近、Scalaの学習を始めて、::(cons)関数。リストの先頭に追加します。
本「Programming in Scala」では、リストへの追加はパフォーマンスo(n)であるのに対し、先頭に追加するとo(1 )

その声明について何かが私を間違っているように思わせます。

パフォーマンスは実装に依存していませんか?順方向リンクと逆方向リンクの両方を使用してリストを単純に実装し、最初と最後の要素をコンテナーに格納することはできませんか?

2つ目の質問は、リストが1、2、3で、リストの最後に4を追加したい場合にどうすればよいでしょうか。

66
Travis Dixon

重要なのはx :: somelistsomelistを変更しませんが、代わりにxとそれに続くsomelistのすべての要素を含む新しいリストを作成します。これは、O(1)時間で実行できます。これは、新しく作成された単一リンクリストのsomelistの後続としてxを設定するだけでよいためです。

代わりに二重リンクリストを使用した場合、xsomelistを変更するsomelistのヘッドの先行として設定する必要があります。だから私たちができるようにしたいなら:: in O(1)元のリストを変更せずに、単一リンクリストのみを使用できます。

2番目の質問について::::は、単一要素のリストをリストの最後に連結します。これはO(n)操作です。

List(1,2,3) ::: List(4)
76
sepp2k

他の回答は、この現象をうまく説明しています。サブルーチンのリストに多くのアイテムを追加する場合、または要素を追加してリストを作成する場合、機能的な慣用法は、リストを逆の順序で作成し、リストの前、最後に逆にします。これにより、O(n²)の代わりにO(n)パフォーマンスが得られます。

18
Chris Conway

質問が更新されたばかりなので、ここで物事が変更されたことに注目する価値があります。

今日のScalaでは、単にxs :+ xは、順次コレクションの最後にアイテムを追加します。 (もあります x +: xsを先頭に追加します。 Scalaの2.8以降のコレクション操作の多くのニーモニックは、 colの隣に行く col選挙。)

これは、ListまたはSeqのデフォルトのリンクされた実装ではO(n)になりますが、VectorまたはIndexedSeq、これは実質的に一定の時間になります。 ScalaのVectorは、最近のほとんど役に立たないJavaのVectorとは異なり、おそらくScalaの最も便利なリストのようなコレクションです。

Scala 2.8以上で作業している場合、 コレクションの紹介 は絶対に読む必要があります。

13
Sarah G

プリペンディングは2つの操作のみを必要とするため、より高速です。

  1. 新しいリストノードを作成する
  2. 新しいノードが既存のリストを指すようにする

ヘッドへのポインタしかないため、リストの最後まで走査する必要があるため、追加にはさらに操作が必要です。

私はScalaでプログラムしたことがありませんが、 List Buffer を試すことができます

12
skalb

ほとんどの関数型言語は、便利な不変のコレクション型であるため、シングルリンクリストのデータ構造を際立たせています。関数型言語で「リスト」と言うと、通常、それが意味します(単一リンクリスト、通常は不変)。そのような型の場合、appendはO(n)ですが、consはO(1)です。

5
Brian