web-dev-qa-db-ja.com

Scala 2.8コレクションデザインチュートリアル

my breathless confusion に続いて、新しい Scala2.8コレクションライブラリが構造化されました。以下がどのように組み合わされるかについていくつかの情報を見つけたいです。

  • コレクションクラス/特性自体(例:ListIterable
  • Likeクラスが存在する理由(例:TraversableLike
  • コンパニオンメソッドの目的(例:List.companion
  • 特定の時点でスコープ内にあるimplicitオブジェクトを知る方法
73
oxbow_lakes

序文

Martin Oderskyによる 2.8コレクションのウォークスルー があり、これがおそらく最初の参照になるはずです。 architectural notes も追加されています。これは、独自のコレクションをデザインしたい人にとって特に興味深いものです。

この回答の残りの部分は、そのようなものが存在する前(実際には2.8.0自体がリリースされる前)に書かれました。

それについての論文は Scala SID# として見つけることができます。その分野の他の論文は、Scala 2.7と2.8の違いに興味がある人々にとっても興味深いでしょう。

論文から引用し、選択的に、そして私の考えを補足する。 decodified.comのMatthiasによって生成されたいくつかの画像もあり、元のSVGファイルは here で見つけることができます。

コレクションクラス/特性自体

コレクションには、実際には3つの特性階層があります。1つは変更可能なコレクション、1つは不変のコレクション、もう1つはコレクションについて何も想定していません。

Scala 2.9で導入された、並列、シリアル、多分並列のコレクションの違いもあります。これらについては、次のセクションで説明します。このセクションで説明する階層は、 非並列コレクション専用

次の画像は、Scala 2.8で導入された非特定の階層を示しています。 General collection hierarchy

示されているすべての要素は特性です。他の2つの階層には、特性を直接継承するクラスと、ラッパークラスへの暗黙的な変換によってその階層に属するviewed asが可能なクラスもあります。これらのグラフの凡例は、それらの後にあります。

不変階層のグラフ: Immutable collection hierarchy

可変階層のグラフ: Mutable collection hierarchy

伝説:

Graph legend

画像を見ることができない人のために、ここにASCIIコレクション階層の図を示します。

_                    Traversable
                         |
                         |
                      Iterable
                         |
      +------------------+--------------------+
     Map                Set                  Seq
      |                  |                    |
      |             +----+----+         +-----+------+
    Sorted Map  SortedSet   BitSet   Buffer Vector LinearSeq
_

並列コレクション

Scala 2.9が並列コレクションを導入した場合、設計目標の1つは、それらの使用を可能な限りシームレスにすることでした。最も簡単に言えば、非並列(シリアル)コレクションを並列コレクションに置き換えることができます。 1つで、すぐにメリットを享受できます。

ただし、それまでのすべてのコレクションはシリアルだったため、それらを使用する多くのアルゴリズムは、それらがあったシリアルであると想定し、それに依存していました。このような前提でメソッドに供給された並列コレクションは失敗します。このため、前のセクションで説明したすべての階層シリアル処理が必須です。

並列コレクションをサポートするために、2つの新しい階層が作成されました。

並列コレクション階層は、特性の名前は同じですが、前にParが付いています:ParIterableParSeqParMapParSet。並列アクセスをサポートするコレクションは、より強力なParTraversable特性をサポートできるため、ParIterableがないことに注意してください。また、シリアル階層に存在する、より特化した特性もいくつかありません。この階層全体は、ディレクトリ_scala.collection.parallel_の下にあります。

並列コレクションを実装するクラスも異なります。可変および不変の両方の並列コレクションのParHashMapおよびParHashSetに加えて、_immutable.ParSeq_を実装するParRangeおよびParVectorおよび_mutable.ParSeq_を実装するParArrayです。

シリアルおよびパラレルコレクションの特性を反映する別の階層も存在しますが、プレフィックスGenGenTraversableGenIterableGenSeqGenMapおよびGenSetが付いています。これらの特性はparentsであり、パラレルコレクションとシリアルコレクションの両方に対応します。つまり、Seqを取得するメソッドは並列コレクションを受け取ることができませんが、GenSeqを取得するメソッドは、シリアルコレクションと並列コレクションの両方で機能することが期待されます。

これらの階層の構造を考えると、Scala 2.8向けに記述されたコードはScala 2.9と完全に互換性があり、シリアル動作を要求しました。書き直さない限り、並列コレクションの利点ですが、必要な変更はごくわずかです。

並列コレクションの使用

コレクションでparメソッドを呼び出すことにより、コレクションをパラレルコレクションに変換できます。同様に、コレクションでseqメソッドを呼び出すことにより、コレクションをシリアルコレクションに変換できます。

コレクションがすでに要求されたタイプ(パラレルまたはシリアル)であった場合、変換は行われません。ただし、パラレルコレクションでseqを呼び出すか、シリアルコレクションでparを呼び出すと、要求された特性を持つ新しいコレクションが生成されます。

コレクションを非並列コレクションに変換するseqと、コレクションの要素から作成されたtoSeqを返すSeqを混同しないでください。並列コレクションでtoSeqを呼び出すと、シリアルコレクションではなくParSeqが返されます。

主な特徴

多くの実装クラスとサブトレイトがありますが、階層にはいくつかの基本的なトレイトがあり、それぞれがより多くのメソッドまたはより具体的な保証を提供しますが、それらを実装できるクラスの数は減少します。

次のサブセクションでは、主な特性とその背後にある考え方について簡単に説明します。

特性TraversableOnce

このトレイトは、以下に説明するトレイトTraversableによく似ていますが、使用できるという制限がありますonce。つまり、TraversableOncemayで呼び出されたメソッドは、使用できなくなります。

この制限により、同じメソッドをコレクションとIterator間で共有できるようになります。これにより、Iteratorに対応しているがIterator固有のメソッドを使用していないメソッドが実際にすべてのコレクションを処理できるようになり、TraversableOnceを受け入れるように書き換えるとイテレータを使用できます。

TraversableOnceはコレクションとイテレータを統合するため、コレクションのみに関係する前のグラフには表示されません。

トラバース可能

コレクション階層の最上位は、特性Traversableです。その唯一の抽象的な操作は

_def foreach[U](f: Elem => U)
_

この操作は、コレクションのすべての要素をトラバースし、指定された操作fを各要素に適用することを目的としています。アプリケーションは、その副作用のためにのみ行われます。実際、fの関数結果はforeachによって破棄されます。

トラバース可能なオブジェクトには、有限のものと無限のものがあります。無限のトラバース可能なオブジェクトの例は、自然数のストリームStream.from(0)です。メソッドhasDefiniteSizeは、コレクションが無限であるかどうかを示します。 hasDefiniteSizeがtrueを返す場合、コレクションは確かに有限です。 falseが返される場合、コレクションはまだ完全に作成されていないため、無限または有限である可能性があります。

このクラスは、foreach(40以上)の観点から効率的に実装できるメソッドを定義します。

特性反復可能

このトレイトは、すべてのコレクションの要素を1つずつ生成するイテレータを返す抽象メソッドiteratorを宣言します。 foreachIterableメソッドは、iteratorの観点から実装されています。 Iterableのサブクラスは、多くの場合、効率を上げるために直接実装でforeachをオーバーライドします。

クラスIterableは、あまり使用されないメソッドをTraversableに追加します。これは、iteratorが使用可能な場合にのみ効率的に実装できます。それらを以下に要約します。

_xs.iterator          An iterator that yields every element in xs, in the same order as foreach traverses elements.
xs takeRight n       A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined).
xs dropRight n       The rest of the collection except xs takeRight n.
xs sameElements ys   A test whether xs and ys contain the same elements in the same order
_

その他の特徴

Iterableの後には、SeqSetMapの3つの基本特性が継承されます。 3つすべてにapplyメソッドがあり、3つすべてがPartialFunction特性を実装していますが、applyの意味はそれぞれの場合で異なります。

SeqSetMapの意味は直感的です。その後、クラスは、パフォーマンス、およびその結果として利用可能になるメソッドに関して特定の保証を提供する特定の実装に分割されます。 LinearSeqIndexedSeqSortedSetなど、さらに改良された特性もあります。

以下のリストは改善される可能性があります。提案とともにコメントを残してください。修正します。

基本クラスと特性

  • Traversable-基本的なコレクションクラス。 foreach。だけで実装できます
    • TraversableProxy-Traversableのプロキシ。 selfに実際のコレクションを指定するだけです。
    • TraversableView-いくつかの非厳密なメソッドを持つTraversable。
    • TraversableForwarder-underlyingtoStringhashCodeequalsstringPrefixnewBuilderを除くほとんどのメソッドをviewに転送し、すべての呼び出しで同じ種類の新しい反復可能なオブジェクトを作成します。
    • _mutable.Traversable_および_immutable.Traversable_-Traversableと同じですが、コレクション型を制限します。
    • Iterableなど、他の特殊なケースMetaDataクラスが存在します。
    • Iterable-Iteratorを作成できるコレクション(iteratorを使用)。
      • IterableProxyIterableViewmutable、および_immutable.Iterable_。
  • Iterator-Traversableの子孫ではない特性nexthasNextを定義します。
    • CountedIterator-Iteratorを定義するcountは、これまでに見た要素を返します。
    • BufferedIterator-headを定義し、次の要素を消費せずに返します。
    • Iteratorなど、他の特殊なケースSourceクラスが存在します。

マップ

  • Map-_Tuple2_のIterable。これは、キー(タプルの最初の要素)を指定して値(タプルの2番目の要素)を取得するためのメソッドも提供します。 PartialFunctionも拡張します。
    • MapProxy-ProxyMap
    • DefaultMap-Mapの抽象メソッドの一部を実装する特性。
    • SortedMap-キーがソートされるMap
      • _immutable.SortMap_
        • _immutable.TreeMap_-_immutable.SortedMap_を実装するクラス。
    • _immutable.Map_
      • _immutable.MapProxy_
      • _immutable.HashMap_-キーハッシュを介して_immutable.Map_を実装するクラス。
      • _immutable.IntMap_-Intキーに特化した_immutable.Map_を実装するクラス。キーの2進数に基づくツリーを使用します。
      • _immutable.ListMap_-リストを通じて_immutable.Map_を実装するクラス。
      • _immutable.LongMap_-Longキーに特化した_immutable.Map_を実装するクラス。 IntMapを参照してください。
      • 特定の数の要素用に最適化された追加のクラスがあります。
    • _mutable.Map_
      • _mutable.HashMap_-キーハッシュを介して_mutable.Map_を実装するクラス。
      • _mutable.ImmutableMapAdaptor_-既存の_mutable.Map_から_immutable.Map_を実装するクラス。
      • _mutable.LinkedHashMap_-?
      • _mutable.ListMap_-リストを通じて_mutable.Map_を実装するクラス。
      • _mutable.MultiMap_-各キーに対して複数の異なる値を受け入れるクラス。
      • _mutable.ObservableMap_-A mixinは、Mapと混合すると、Publisherインターフェースを介してオブザーバーにイベントを発行します。
      • _mutable.OpenHashMap_-オープンハッシュアルゴリズムに基づくクラス。
      • _mutable.SynchronizedMap_-A mixinMapと混合して、同期されたメソッドを備えたバージョンを提供する必要があります。
      • _mutable.MapProxy_。

シーケンス

  • Seq-要素のシーケンス。明確に定義されたサイズと要素の繰り返しを想定しています。 PartialFunctionも拡張します。
    • IndexedSeq-O(1)要素へのアクセスとO(1)長さの計算。をサポートするシーケンス
      • IndexedSeqView
      • _immutable.PagedSeq_-IndexedSeqの実装。要素は、コンストラクタを介して渡される関数によってオンデマンドで生成されます。
      • _immutable.IndexedSeq_
        • _immutable.Range_-区切られた整数のシーケンス。下端で閉じ、上端で開き、ステップ付き。
          • _immutable.Range.Inclusive_-Rangeもハイエンドでクローズされました。
          • _immutable.Range.ByOne_-ステップが1のRange.
        • _immutable.NumericRange_-Rangeで機能するIntegralのより一般的なバージョン。
          • _immutable.NumericRange.Inclusive_、_immutable.NumericRange.Exclusive_。
          • _immutable.WrappedString_、_immutable.RichString_-Stringメソッドを保持しながら、Stringを_Seq[Char]_として表示できるラッパー。両者の違いがよくわかりません。
      • _mutable.IndexedSeq_
        • _mutable.GenericArray_-Seqベースの配列のような構造。 「クラス」ArrayはJavaのArrayであり、クラスよりもメモリストレージメソッドであることに注意してください。
        • _mutable.ResizableArray_-サイズ変更可能な配列に基づくクラスによって使用される内部クラス。
        • _mutable.PriorityQueue_、_mutable.SynchronizedPriorityQueue_-優先キューを実装するクラス-最初にOrdering、最後にキューイングの順序に従って要素がデキューされるキュー。
        • _mutable.PriorityQueueProxy_-Proxyの抽象PriorityQueue
    • LinearSeq-isEmptyheadtail。の効率的な時間を使用した線形シーケンスの特性
      • _immutable.LinearSeq_
        • _immutable.List_-不変の単一リンクリスト実装。
        • _immutable.Stream_-遅延リスト。その要素はオンデマンドでのみ計算されますが、後でメモされます(メモリに保持されます)。理論的には無限にできます。
      • _mutable.LinearSeq_
        • _mutable.DoublyLinkedList_-可変prevheadelem)およびtailnext)を含むリスト。
        • _mutable.LinkedList_-可変headelem)およびtailnext)を含むリスト。
        • _mutable.MutableList_-可変リストに基づいてクラスを実装するために内部的に使用されるクラス。
          • _mutable.Queue_、_mutable.QueueProxy_-FIFO(先入れ先出し)操作に最適化されたデータ構造。
          • _mutable.QueueProxy_-_mutable.Queue_のProxy
    • SeqProxySeqViewSeqForwarder
    • _immutable.Seq_
      • _immutable.Queue_-FIFO最適化(先入れ先出し)データ構造を実装するクラス。 mutableimmutableの両方のキューに共通のスーパークラスはありません。
      • _immutable.Stack_-LIFOに最適化された(後入れ先出し)データ構造を実装するクラス。 mutableimmutableスタックの両方に共通のスーパークラスはありません。
      • _immutable.Vector_-?
      • _scala.xml.NodeSeq_-_immutable.Seq_を拡張する特殊なXMLクラス。
      • _immutable.IndexedSeq_-上記のとおり。
      • _immutable.LinearSeq_-上記のとおり。
    • _mutable.ArrayStack_-配列を使用してLIFO最適化データ構造を実装するクラス。おそらく、通常のスタックよりも大幅に高速です。
    • _mutable.Stack_、_mutable.SynchronizedStack_-LIFOに最適化されたデータ構造を実装するクラス。
    • _mutable.StackProxy_-_mutable.Stack_のProxy ..
    • _mutable.Seq_
      • _mutable.Buffer_-新しいメンバーの追加、前置、または挿入によって変更できる一連の要素。
        • _mutable.ArrayBuffer_-_mutable.Buffer_クラスの実装で、追加、更新、ランダムアクセス操作の償却時間が一定です。 NodeBufferなど、いくつかの特殊なサブクラスがあります。
        • _mutable.BufferProxy_、_mutable.SynchronizedBuffer_。
        • _mutable.ListBuffer_-リストに基づくバッファ。それは一定時間の追加とプリペンドを提供し、他のほとんどの操作は線形です。
        • _mutable.ObservableBuffer_-A mixinトレイトは、Bufferと混合すると、Publisherインターフェイスを介して通知イベントを提供します。
        • _mutable.IndexedSeq_-上記のとおり。
        • _mutable.LinearSeq_-上記のとおり。

セット

  • Set-セットは、多くても1つのオブジェクトを含むコレクションです。
    • BitSet-ビットセットとして保存される整数のセット。
      • _immutable.BitSet_
      • _mutable.BitSet_
    • SortedSet-要素が順序付けられているセット。
      • _immutable.SortedSet_
        • _immutable.TreeSet_-ツリーに基づくSortedSetの実装。
    • SetProxy-ProxySet
    • _immutable.Set_
      • _immutable.HashSet_-要素のハッシュに基づくSetの実装。
      • _immutable.ListSet_-リストに基づくSetの実装。
      • 追加のセットクラスが存在し、0から4要素のセットの最適化された実装を提供します。
      • _immutable.SetProxy_-不変のProxySet
    • _mutable.Set_
      • _mutable.HashSet_-要素のハッシュに基づくSetの実装。
      • _mutable.ImmutableSetAdaptor_-不変のSetから変更可能なSetを実装するクラス。
      • LinkedHashSet-リストに基づくSetの実装。
      • ObservableSet-A mixinトレイトは、Setと混合すると、Publisherインターフェイスを介して通知イベントを提供します。
      • SetProxy-ProxySet
      • SynchronizedSet-A mixinトレイトは、Setと混合すると、Publisherインターフェイスを介して通知イベントを提供します。

  • Likeクラスが存在する理由(例:TraversableLike)

これは、コードを最大限に再利用するために行われました。特定の構造(トラバーサブル、マップなど)を持つクラスの具体的なgeneric実装は、Likeクラスで行われます。一般的な消費を目的としたクラスは、最適化できる選択されたメソッドをオーバーライドします。

  • コンパニオンメソッドの目的(例:List.companion)

クラスのビルダー、つまり、mapのようなメソッドで使用できる方法でそのクラスのインスタンスを作成する方法を知っているオブジェクトは、コンパニオンオブジェクトのメソッドによって作成されます。したがって、タイプXのオブジェクトを構築するには、Xのコンパニオンオブジェクトからそのビルダーを取得する必要があります。残念ながら、Scalaでは、クラスXからオブジェクトXに取得する方法がありません。そのため、 Xの各インスタンスで定義されたメソッドcompanion。これは、クラスXのコンパニオンオブジェクトを返します。

通常のプログラムではこのような方法がいくらか使用される可能性がありますが、その目的は、コレクションライブラリでコードを再利用できるようにすることです。

  • 特定の時点でスコープ内にある暗黙のオブジェクトを知る方法

あなたはそれを気にする必要はありません。これらは暗黙的に行われるため、どのように機能させるかを理解する必要はありません。

これらの暗黙は、コレクションのメソッドを親クラスで定義できるようにするために存在しますが、同じタイプのコレクションを返します。たとえば、mapメソッドはTraversableLikeで定義されていますが、Listで使用すると、Listが返されます。

188