Scalaコレクション:秘密と秘trick

Pavel Fatin Scala Collections Tips and Tricksの記事の翻訳を紹介します。 PavelはJetBrainsで働いており、IntelliJ IDEAのScalaプラグインを開発しています 。 さらに、物語は著者に代わっています。







この記事では、ScalaコレクションAPIの日常的な使用に共通する単純化と最適化について説明します







一部のヒントは、コレクションライブラリの実装の複雑さに基づいていますが、ほとんどのレシピは、実際には見落とされがちなスマートな変換です。







このリストは、 Scala IntelliJプラグインのために、 Scala コレクションの実用的な検査を開発しようとする私の試みに触発されました。 現在、これらの検査を実装しているので、IDEAでScalaプラグインを使用すると、静的コード分析から自動的に恩恵を受けます。







ただし、これらのレシピはそれ自体で価値があります。 これらは、Scala標準コレクションライブラリの理解を深め、コードをより高速で表現力豊かにするのに役立ちます。







更新:

冒険心があるなら、

適切なインスペクションを選択することで、 Scala用のIntelliJプラグインの開発を支援し、実装を試す方法を学ぶことができます。







内容:







1.  2.  3.   4.  (Sequences) 4.1.  4.2.  4.3.  4.4.  4.5.  4.6.  4.7.  4.8.  4.9.  4.10.  5.  (Sets) 6. Option- 6.1.  6.2. Null 6.3.  6.4.  7.  8. 
      
      





すべてのサンプルコードはGitHubリポジトリから入手できます







1.凡例



コード例をわかりやすくするために、次の表記を使用しました。









2.構成



レシピは孤立していて自給自足であるという事実にもかかわらず、次のより高度な表現への漸進的な変換のために調整できることを忘れないでください。







 seq.filter(_ == x).headOption != None //  seq.filter(p).headOption  seq.find(p) seq.find(_ == x) != None //  option != None  option.isDefined seq.find(_ == x).isDefined //  seq.find(p).isDefined  seq.exists(p) seq.exists(_ == x) //  seq.exists(_ == x)  seq.contains(x) seq.contains(x)
      
      





そのため、「交換処方アプリケーションモデル」( SICPに類似)に依存し、それを使用して複雑な表現を簡素化できます。







3.副作用



「サイド効果」は、主要な変換をリストする前に考慮すべき基本的な概念です。







実際、副作用とは、値を返すことに加えて、関数または式の外側で観察されるアクションのことです。例えば:









上記のアクションのいずれかを含む関数または式は副作用があると言われ、そうでなければ「純粋」と呼ばれます。







なぜ副作用がそれほど重要なのですか? 彼らと実行の順序が重要だからです。 たとえば、2つの「純粋な」式(対応する値に関連付けられています):







 val x = 1 + 2 val y = 2 + 3
      
      





それらには副作用(すなわち、式の外側で観察される効果)が含まれていないため、これらの式を任意の順序で計算できます-最初にx



、次にy



または最初にy



、次にx



これは得られた結果の正確さに影響しません(必要に応じて、結果の値をキャッシュすることもできます)。 次に、次の変更を検討します。







 val x = { print("foo"); 1 + 2 } val y = { print("bar"); 2 + 3 }
      
      





これは別の話です-ターミナルでは「foobar」の代わりに「barfoo」を出力するため、実行順序を変更することはできません(これは明らかに意図したものではありません)。







そのため、副作用の存在は、コードに適用できる変換 (単純化と最適化の両方) の数を減らします。







同様の考慮事項は、関連するコレクション、式にも適用されます。 範囲外のどこかに特定のbuilder



があると想像してください:







 seq.filter(x => { builder.append(x); x > 3 }).headOption
      
      





原則として、 seq.filter(p).headOption



呼び出しはseq.filter(p).headOption



簡略化されていますが、副作用seq.find(p)



ため、これを行うseq.find(p)



ん。







 seq.find( x => {builder.append(x); x > 3 })
      
      





これらの式は最終値の点では同等ですが、副作用の点では同等ではありません。 最初の式はすべての要素を追加し、最後の式は述語に一致する最初の値を見つけるとすぐにすべての要素を破棄します。 したがって、このような単純化はできません。







自動簡素化を可能にするために何ができますか? 答えは、コード内のすべての副作用(原則としてコレクションがないものを含む)に関して従うべき黄金律です:









したがって、 builder



aを(副作用があるAPIとともに)取り除くか、 builder



aの呼び出しを純粋な式から分離する必要があります。 このbuilder



は、削除できないサードパーティのオブジェクトであると想定します。そのため、呼び出しのみを分離できます。







 seq.foreach(builder.append) seq.filter(_ > 3).headOption
      
      





これで、安全に変換を実行できます。







 seq.foreach(builder.append) seq.find(x > 3)
      
      





清潔で美しい! 副作用の分離により、自動変換が可能になりました。 追加の利点は、明確な分離が存在するため、結果のコードを人が理解しやすくなることです。







副作用を分離することの最も明白で、同時に最も重要な利点は、他の可能な最適化に関係なく、コードの信頼性を向上させることです。 例について:初期式は、 Seq



現在の実装に応じて、さまざまな副作用を引き起こす可能性があります。 たとえば、 Vector



場合、すべての要素が追加され、 Stream



場合、述語との最初の一致が成功した後、すべての要素がスキップされます(ストリームは「遅延」であるため、要素は必要な場合にのみ計算されます)。 副作用の分離により、これらの不確実性を回避できます。







4.シーケンス



このセクションのアドバイスはSeq



相続人に適用されますが、一部の変換は他のタイプのコレクション(コレクションではなく)に有効です。たとえば、 Set



Option



Map



さらにはIterator



(これらはすべてモナドメソッドと同様のインターフェースを提供するため)







4.1作成



空のコレクションを明示的に作成する



 //  Seq[T]() //  Seq.empty[T]
      
      





一部の不変のコレクションクラスには、 empty



メソッドのシングルトン実装がありempty



。 ただし、すべてのファクトリメソッドが作成されたコレクションの長さをチェックするわけではありません。 そのため、コンパイル段階でvoidを指定することにより、ヒープ上のスペース(インスタンスの再利用による)またはプロセッサーサイクル(実行時の次元チェックに費やすことができる)を節約できます。

以下にも適用可能: Set



Option



Map



Iterator









4.2長さ



配列の場合、 size



代わりにlength



を使用しsize





 //  array.size //  array.length
      
      





size



length



は本質的に同義ですが、Scala 2.11ではArray.size



呼び出しは暗黙の変換によって行われ、各メソッド呼び出しの中間ラッパーオブジェクトを作成します。 もちろん、JVMのエスケープ分析を有効にしない限り、一時オブジェクトはガベージコレクターの負担になり、コードのパフォーマンスを低下させます(特にループ内)。







isEmptyを拒否しないでください



 //  !seq.isEmpty !seq.nonEmpty //  seq.nonEmpty seq.isEmpty
      
      





単純なプロパティは、複合式よりも視覚的なノイズが少ないです。

以下にも適用可能: Set



Option



Map



Iterator









voidをチェックするときに長さを計算しないでください。



 //  seq.length > 0 seq.length != 0 seq.length == 0 //  seq.nonEmpty seq.nonEmpty seq.isEmpty
      
      





一方では、単純なプロパティは複合式よりもはるかに簡単に認識されます。 一方、 LinearSeq



相続人( List



など)は、リストの長さを計算するために( IndexedSeq



O(1)



代わりにO(n)



時間を必要とする場合があるため、一般的に、長さの計算を避けることでコードを高速化できます、この値はあまり必要ありません。

無限ストリームの.length



呼び出しは決して終了しない可能性があるため、常に明示的にストリームの空性を確認してください。

適用対象: Set



Map









比較中にコレクションのフルサイズを計算しないでください。



 //  seq.length > n seq.length < n seq.length == n seq.length != n //  seq.lengthCompare(n) > 0 seq.lengthCompare(n) < 0 seq.lengthCompare(n) == 0 seq.lengthCompare(n) != 0
      
      





コレクションのサイズの計算は、一部のタイプのコレクションでは非常に「高価な」計算になる可能性があるため、 LinearSeq



Seq



値で非表示にできるO(length min n)



の比較時間をO(length)



からO(length min n)



LinearSeq



できます。 さらに、無限のストリームを処理する場合、このアプローチは不可欠です。







emptyをチェックするためにexists



を使用しないでください。



 //  seq.exists(_ => true) seq.exists(const(true)) //  seq.nonEmpty
      
      





もちろん、このようなトリックは完全に冗長です。

適用対象:セット、オプション、マップ、イテレーター。







4.3平等



配列の内容を比較するために==



に依存しないでください



 //  array1 == array2 //  array1.sameElements(array2)
      
      





等価性テストは、配列のさまざまなインスタンスに対して常にfalse



を返します。

該当するもの: Iterator









異なるカテゴリのコレクションの同等性をテストしないでください



 //  seq == set //  seq.toSet == set
      
      





等価性チェックを使用して、コレクションと異なるカテゴリ(例: List



Set



)を比較できます。

このチェックの意味について再考してください(上記の例-シーケンス内の重複をどのように考慮するか)。







sameElements



を使用して通常のコレクションを比較しないでください



 //  seq1.sameElements(seq2) //  seq1 == seq2
      
      





同等性テストは、同じカテゴリのコレクションを比較する方法です。 理論的には、これにより、潜在的なインスタンスチェックが原因でパフォーマンスが向上することがあります( eq



、通常ははるかに高速)。







明示的に対応を使用しないでください



 //  seq1.corresponds(seq2)(_ == _) //  seq1 == seq2
      
      





同じことを行う組み込みメソッドが既にあります。 両方の式では、要素の順序が考慮されます。 繰り返しになりますが、生産性の向上の恩恵を受けることができます。







4.4索引付け



インデックスで最初のアイテムを取得しないでください



 //  seq(0) //  seq.head
      
      





一部のコレクションクラスでは、更新されたアプローチが少し速くなる場合があります(たとえば、 List.apply



コードを確認してください)。 さらに、プロパティへのアクセスは、引数を使用してメソッドを呼び出すよりもはるかに簡単です(構文的にも意味的にも)。







インデックスで最後のアイテムを取得しないでください



 //  seq(seq.length - 1) //  seq.last
      
      





最後の式はより明確で、コレクションの長さの不必要な計算を避けます(そして線形シーケンスの場合、これには多くの時間がかかります)。 さらに、一部のコレクションクラスは、インデックスアクセスよりも効率的に最後のアイテムを取得できます。







コレクション内のインデックスを明示的にチェックしないでください



 //  if (i < seq.length) Some(seq(i)) else None //  seq.lift(i)
      
      





意味的には、2番目の式は同等ですが、より表現的には







headOptionをエミュレートしないでください



 //  if (seq.nonEmpty) Some(seq.head) else None seq.lift(0) //  seq.headOption
      
      





簡略化された式はより簡潔です。







lastOption



エミュレートしない



 //  if (seq.nonEmpty) Some(seq.last) else None seq.lift(seq.length - 1) //  seq.lastOption
      
      





最後の式は短くなっています(そして潜在的に高速です)。







indexOf



およびlastIndexOf



引数タイプに注意してください



 //  Seq(1, 2, 3).indexOf("1") //  Seq(1, 2, 3).lastIndexOf("2") //  //  Seq(1, 2, 3).indexOf(1) Seq(1, 2, 3).lastIndexOf(2)
      
      





分散indexOf



により、 indexOf



メソッドとlastIndexOf



メソッドはAny



型の引数を受け入れます。 実際には、これにより、コンパイル段階で検出できないバグを見つけにくくなります。 IDEのサポート検査が行われる場所です。







シーケンスインデックスの範囲を手動で作成しないでください



 //  Range(0, seq.length) //  seq.indices
      
      





シーケンス内のすべてのインデックスの範囲を返す組み込みメソッドがあります。







コレクションをインデックスに手動で関連付けるためにzipを使用しないでください



 //  seq.zip(seq.indices) //  seq.zipWithIndex
      
      





まず、最後の式が短くなります。 さらに、コレクションのサイズの隠された計算(線形シーケンスの場合は高価になる可能性がある)を避けることにより、パフォーマンスの向上が期待できます。

後者の式の追加の利点は、無限のコレクション(例: Stream



)でうまく機能することです。







IndexedSeqインスタンスを関数オブジェクトとして使用します。



 //  (seq: IndexedSeq[T]) Seq(1, 2, 3).map(seq(_)) //  Seq(1, 2, 3).map(seq)
      
      





IndexedSeq[T]



インスタンスはFunction1[Int, T]



でもあるため、そのまま使用できます。







4.5存在



比較述語を使用して要素をチェックしないでください



 //  seq.exists(_ == x) //  seq.contains(x)
      
      





2番目の式は意味的に同等ですが、より表現力豊かです。 これらの式がSet



に適用されると、セット検索はO(1)



なる傾向があるため、パフォーマンスが劇的に異なる可能性がありますO(1)



exists



場合exists



内部インデックスが使用されないため)。

以下にも適用可能: Set



Option



Iterator









contains



れる引数のタイプに注意してください



 //  Seq(1, 2, 3).contains("1") //  //  Seq(1, 2, 3).contains(1)
      
      





indexOf



lastIndexOf



ように、タイプAny



引数を取るメソッドがcontains



ます。これにより、コンパイル段階では検出されないバグを見つけにくくなります。 それらに注意してください。







不等式述部を使用して、欠落している要素をチェックしないでください



 //  seq.forall(_ != x) //  !seq.contains(x)
      
      





繰り返しますが、最後の式はよりクリーンで、おそらくより高速です(特にセットの場合)。

以下にも適用可能: Set



Option



Iterator









発生をカウントして存在を確認しない



 //  seq.count(p) > 0 seq.count(p) != 0 seq.count(p) == 0 //  seq.exists(p) seq.exists(p) !seq.exists(p)
      
      





明らかに、条件に一致するアイテムがコレクション内にあるかどうかを知る必要がある場合、一致するアイテムの数をカウントすることは冗長です。 簡略化された式は、よりクリーンで高速に見えます。









存在を確認するためにフィルタリングに頼らないでください



 //  seq.filter(p).nonEmpty seq.filter(p).isEmpty //  seq.exists(p) !seq.exists(p)
      
      





filter



呼び出しは、ヒープのスペースを占有してGCをロードする中間コレクションを作成します。 さらに、前述の式はすべてのオカレンスを検出しますが、最初の式のみを検出する必要があります(コレクションの可能な内容によってはコードが遅くなる可能性があります)。 遅延コレクション( Stream



や、特にIterator



)の場合、潜在的なパフォーマンスの向上はそれほど重要ではありません。









存在を確認するために検索に頼らないでください



 //  seq.find(p).isDefined seq.find(p).isEmpty //  seq.exists(p) !seq.exists(p)
      
      





検索はフィルタリングよりも間違いなく優れていますが、これは制限からはほど遠いです(少なくとも明確さの点では)。

以下にも適用可能: Set



Option



Map



Iterator









4.6フィルタリング



フィルター述語を拒否しないでください



 //  seq.filter(!p) //  seq.filterNot(p)
      
      





最後の式は構文的に単純です(意味的に同等であるという事実にもかかわらず)。

以下にも適用可能: Set



Option



Map



Iterator









カウントするためにフィルタリングしないでください



 //  seq.filter(p).length //  seq.count(p)
      
      





filter



呼び出しは、中間の(あまり必要ではない)コレクションを作成します。これは、ヒープのスペースを占有し、GCをロードします。

以下にも適用可能: Set



Option



Map



Iterator









最初の出現を見つけるためにフィルタリングを使用しないでください



 //  seq.filter(p).headOption //  seq.find(p)
      
      





もちろん、 seq



遅延コレクション( Stream



など)でない場合、最初の要素のみが必要であるという事実にもかかわらず、フィルタリングはすべての発生を検出(および一時コレクションを作成)します。

以下にも適用可能: Set



Option



Map



Iterator









4.7並べ替え



プロパティで手動で並べ替えないでください



 //  seq.sortWith(_.property < _.property) //  seq.sortBy(_.property)
      
      





これを行うには、より明確で表現力豊かな独自の方法があります。







IDで手動で並べ替えないでください



 //  seq.sortBy(identity) seq.sortWith(_ < _) //  seq.sorted
      
      





そして、このための方法もあります。







ワンステップで逆ソート



 //  seq.sorted.reverse seq.sortBy(_.property).reverse seq.sortWith(f(_, _)).reverse //  seq.sorted(Ordering[T].reverse) seq.sortBy(_.property)(Ordering[T].reverse) seq.sortWith(!f(_, _))
      
      





したがって、中間コレクションの作成を回避し、追加の変換を排除できます(ヒープスペースとプロセッササイクルを節約するため)。







ソートを使用して最小要素を見つけないでください



 //  seq.sorted.head seq.sortBy(_.property).head //  seq.min seq.minBy(_.property)
      
      





後者のアプローチはより表現力豊かです。 さらに、追加のコレクションが作成されないという事実により、より高速に動作します。







最大の要素を見つけるためにソートを使用しないでください



 //  seq.sorted.last seq.sortBy(_.property).last //  seq.max seq.maxBy(_.property)
      
      





説明は前のヒントと同じです。







4.8畳み込み



金額を手動で計算しないでください



 //  seq.reduce(_ + _) seq.fold(z)(_ + _) //  seq.sum seq.sum + z
      
      





このアプローチの利点は、明快さと表現力です。









作業を手動で計算しないでください



 //  seq.reduce(_ * _) seq.fold(z)(_ * _) //  seq.product seq.product * z
      
      





理由は、前のケースと同じです。









最小アイテムを手動で検索しないでください



 //  seq.reduce(_ min _) seq.fold(z)(_ min _) //  seq.min z min seq.min
      
      





理論的根拠は前のケースと同じです。

該当するもの: Set



Iterator









最大アイテムを手動で検索しないでください



 //  seq.reduce(_ max _) seq.fold(z)(_ max _) //  seq.max z max seq.max
      
      





すべて前のケースと同じです。

該当するもの: Set



Iterator









forall



エミュレートしない



 //  seq.foldLeft(true)((x, y) => x && p(y)) !seq.map(p).contains(false) //  seq.forall(p)
      
      





簡素化の目標は、明快さと表現力です。









エミュレートしない



 //  seq.foldLeft(false)((x, y) => x || p(y)) seq.map(p).contains(true) //  seq.exists(p)
      
      





すべての明快さと表現力で、最後の式はより高速に動作し(最初の適切な要素が見つかるとすぐに後続の要素の処理を停止します)、無限のシーケンスで機能します。









map



エミュレートしません



 //  seq.foldLeft(Seq.empty)((acc, x) => acc :+ f(x)) seq.foldRight(Seq.empty)((x, acc) => f(x) +: acc) //  seq.map(f)
      
      





これは、畳み込みによるマップ(マップ)の関数型プログラミング実装の「クラシック」です。 間違いなく有益であるが、使用する必要はない。 これを行うには、組み込みの表現力豊かなメソッドを使用します(これは、実装で単純なwhile



使用するため、高速です)。

以下にも適用可能: Set



Option



Iterator









filter



エミュレートしない



 //  seq.foldLeft(Seq.empty)((acc, x) => if (p(x)) acc :+ x else acc) seq.foldRight(Seq.empty)((x, acc) => if (p(x)) x +: acc else acc) //  seq.filter(p)
      
      





理由は、前のケースと同じです。

以下にも適用可能: Set



Option



Iterator









reverse



エミュレートしない



 //  seq.foldLeft(Seq.empty)((acc, x) => x +: acc) seq.foldRight(Seq.empty)((x, acc) => acc :+ x) //  seq.reverse
      
      





繰り返しになりますが、組み込みのメソッドはより高速でクリーンです。

以下にも適用可能: Set



Option



Iterator









4.9比較



Scalaでのパターンマッチング部分関数のスタンドアロンのヒントを次に示します







パターンマッチングを持つ関数の代わりに部分関数を使用する



 //  seq.map { _ match { case P => ??? // x N } } //  seq.map { case P => ??? // x N }
      
      





更新された式は同様の結果を与え、よりシンプルに見えます。







上記の変換は、 map



関数の引数だけでなく、任意の関数に適用できます。 このヒントはコレクションに限定されません。 ただし、ScalaコレクションライブラリAPIの高階関数の普遍性考慮すると、この関数は便利です。







flatMap



collect





 //  seq.flatMap { case P => Seq(???) // x N case _ => Seq.empty } //  seq.collect { case P => ??? // x N }
      
      





.

: Set



, Option



, Map



, Iterator



.







match



collect



,



 //  v match { case P => Seq(???) // x N case _ => Seq.empty } //  Seq(v) collect { case P => ??? // x N }
      
      





, case- , , match



collect



. , case



.

Option



, .

: Set



, Option



, Iterator



.







collectFirst





 //  seq.collect{case P => ???}.headOption //  seq.collectFirst{case P => ???}
      
      





, .









4.10



filter





 //  seq.filter(p1).filter(p2) //  seq.filter(x => p1(x) && p2(x))
      
      





( filter



), .

, ( ), : seq.view.filter(p1).filter(p2).force



.









map





 //  seq.map(f).map(g) //  seq.map(f.andThen(g))
      
      





, .







, view ( ), : seq.view.map(f).map(g).force



.











 //  seq.sorted.filter(p) //  seq.filter(p).sorted
      
      





— . , .









map





 //  seq.reverse.map(f) //  seq.reverseMap(f)
      
      





() , ( List



). , , , .









 //  seq.reverse.iterator //  seq.reverseIterator
      
      





.







Set





 //  seq.toSet.toSeq //  seq.distinct
      
      





( ), .







slice





 //  seq.drop(x).take(y) //  seq.slice(x, x + y)
      
      





, . , .

: Set



, Map



, Iterator



.







splitAt





 //  val seq1 = seq.take(n) val seq2 = seq.drop(n) //  val (seq1, seq2) = seq.splitAt(n)
      
      





( List



, Stream



), - , .

: Set



, Map



.







span





 //  val seq1 = seq.takeWhile(p) val seq2 = seq.dropWhile(p) //  val (seq1, seq2) = seq.span(p)
      
      





, .









partition





 //  val seq1 = seq.filter(p) val seq2 = seq.filterNot(p) //  val (seq1, seq2) = seq.partition(p)
      
      





-,









takeRight





 //  seq.reverse.take(n).reverse //  seq.takeRight(n)
      
      





( , ).







flatten





 //  (seq: Seq[Seq[T]]) seq.reduce(_ ++ _) seq.fold(Seq.empty)(_ ++ _) seq.flatMap(identity) //  seq.flatten
      
      





: .

: Set



, Option



, Iterator



.







flatMap





 //  (f: A => Seq[B]) seq.map(f).flatten //  seq.flatMap(f)
      
      





- . , .

: Set



, Option



, Iterator



.







map





 //  seq.map(???) //   //  seq.foreach(???)
      
      





, map



. , .

: Set



, Option



, Map



, Iterator



.







unzip





 //  (seq: Seq[(A, B]]) seq.unzip._1 //  seq.map(_._1)
      
      





, - .











( ).







1) .







 //  seq.map(f).flatMap(g).filter(p).reduce(???) //  seq.view.map(f).flatMap(g).filter(p).reduce(???)
      
      





reduce



, , : reduceLeft



, reduceRight



, fold



, foldLeft



, foldRight



, sum



, product



, min



, max



, head



, headOption



, last



, lastOption



, indexOf



, lastIndexOf



, find



, contains



, exists



, count



, length



, mkString



, ..







— , , - , GC. , ( map



, flatMap



, filter



, ++,



..) «» ( Stream



) , , .







(view) — , , :









, view



.







2) , .







, - — force



( ):







 //  seq.map(f).flatMap(g).filter(p) //  seq.view.map(f).flatMap(g).filter(p).force
      
      





— , , , withFilter



:







 seq.withFilter(p).map(f)
      
      





"for comprehensions". , — , (, ). , ( ) ( veiw



force



)







, withFilter



- .







3) .







 //  seq.map(f).flatMap(g).filter(p).toList //  seq.view.map(f).flatMap(g).filter(p).toList
      
      





force



-, .







« + ». breakOut



:







 seq.map(f)(collection.breakOut): List[T]
      
      





, :









, , breakOut



.







.











 //  seq = seq :+ x seq = x +: seq seq1 = seq1 ++ seq2 seq1 = seq2 ++ seq1 //  seq :+= x seq +:= x seq1 ++= seq2 seq1 ++:= seq2
      
      





Scala « », « » (“assignment operators”) — x <op>= y



x = x <op> y



, : <op>



(: +



, -



, .). , <op>



:



, - (.. , ). :







 //  list = x :: list list1 = list2 ::: list1 stream = x #:: stream stream1 = stream2 #::: stream1 //  list ::= x list1 :::= list2 stream #::= x stream1 #:::= stream2
      
      





.

Set



, Map



, Iterator



( ).









 //  seq.foldLeft(Set.empty)(_ + _) seq.foldRight(List.empty)(_ :: _) //  seq.toSet seq.toList
      
      





, , . , , .

: Set



, Option



, Iterator



.







toSeq



.



 //  (seq: TraversableOnce[T]) seq.toSeq //  seq.toStream seq.toVector
      
      





- , Seq(...)



( , Vector ), toSeq



( Stream



, Iterator



view



) . TraversableOnce.toSeq



Stream



, , . , , .







:







 val source = Source.fromFile("lines.txt") val lines = source.getLines.toSeq source.close() lines.foreach(println)
      
      





IOException



, , .







, toStream



, , toVector



toSeq



.









 //  (seq: Seq[String]) seq.reduce(_ + _) seq.reduce(_ + separator + _) seq.fold(prefix)(_ + _) seq.map(_.toString).reduce(_ + _) // seq: Seq[T] seq.foldLeft(new StringBuilder())(_ append _) //  seq.mkString seq.mkString(prefix, separator, "")
      
      





, StringBuilder



.









5. (Sets)



. , .







sameElements





 //  set1.sameElements(set2) //  set1 == set2
      
      





( ), .







sameElements



, , .

, : , LinkedHashSet



.

: Map



.







Set -



 //  (set: Set[Int]) Seq(1, 2, 3).filter(set(_)) Seq(1, 2, 3).filter(set.contains) //  Seq(1, 2, 3).filter(set)
      
      





Set[T]



Function1[T, Boolean]



, .









 //  set1.filter(set2.contains) set1.filter(set2) //  set1.intersect(set2) //  set1 & set2
      
      





, .

, , .









 //  set1.filterNot(set2.contains) set1.filterNot(set2) //  set1.diff(set2) //  set1 &~ set2
      
      





, , .

, , .







6. Options



Option



Scala , ( ..) , , - .







Option. , , Option



API.







6.1



Option None





 //  option == None option != None //  option.isEmpty option.isDefined
      
      





, , , Option



.

, Option[T]



T



, scalac ( ), .







Option



Some





 //  option == Some(v) option != Some(v) //  option.contains(v) !option.contains(v)
      
      





.







isInstanceOf





 //  option.isInstanceOf[Some[_]] //  option.isDefined
      
      





.









 //  option match { case Some(_) => true case None => false } option match { case Some(_) => false case None => true } //  option.isDefined option.isEmpty
      
      





, — . , .

: Seq



, Set



.







,



 //  !option.isEmpty !option.isDefined !option.nonEmpty //  seq.isDefined seq.isEmpty seq.isEmpty
      
      





, — , .

, : isDefined



( option) nonEmpty



( ). , Option



.







6.2 Null



null



, Option





 //  if (v != null) Some(v) else None //  Option(v)
      
      





.







null





 //  option.getOrElse(null) //  option.orNull
      
      





, .







6.3



, , Option



.







, Option



, , « Option



map



, flatMap



, filter



foreach



». , "check & get" ( ) , if



.

— , «» :









.







getOrElse





 //  if (option.isDefined) option.get else z option match { case Some(it) => it case None => z } //  option.getOrElse(z)
      
      





orElse





 //  if (option1.isDefined) option1 else option2 option1 match { case Some(it) => Some(it) case None => option2 } //  option1.orElse(option2)
      
      





exists





 //  option.isDefined && p(option.get) if (option.isDefined) p(option.get) else false option match { case Some(it) => p(it) case None => false } //  option.exists(p)
      
      





forall





 //  option.isEmpty || (option.isDefined && p(option.get)) if (option.isDefined) p(option.get) else true option match { case Some(it) => p(it) case None => true } //  option.forall(p)
      
      





contains





 //  option.isDefined && option.get == x if (option.isDefined) option.get == x else false option match { case Some(it) => it == x case None => false } //  option.contains(x)
      
      





foreach





 //  if (option.isDefined) f(option.get) option match { case Some(it) => f(it) case None => } //  option.foreach(f)
      
      





filter





 //  if (option.isDefined && p(option.get)) option else None option match { case Some(it) && p(it) => Some(it) case _ => None } //  option.filter(p)
      
      





map





 //  if (option.isDefined) Some(f(option.get)) else None option match { case Some(it) => Some(f(it)) case None => None } //  option.map(f)
      
      





flatMap





 //  (f: A => Option[B]) if (option.isDefined) f(option.get) else None option match { case Some(it) => f(it) case None => None } //  option.flatMap(f)
      
      





6.4



map



getOrElse



fold





 //  option.map(f).getOrElse(z) //  option.fold(z)(f)
      
      





( z



— ), . (- Scala), .







, - , , .







exists





 //  option.map(p).getOrElse(false) //  option.exists(p)
      
      





( Option



). getOrElse



.







flatten





 //  (option: Option[Option[T]]) option.map(_.get) option.getOrElse(None) //  option.flatten
      
      





.







Option



Seq





 //  option.map(Seq(_)).getOrElse(Seq.empty) option.getOrElse(Seq.empty) // option: Option[Seq[T]] //  option.toSeq
      
      





, .







7.



, , .









 //  map.find(_._1 == k).map(_._2) //  map.get(k)
      
      





, , - , Map



(, ) — . , .







get



,



 // Before map.get(k).get // After map(k)
      
      





Option



, (raw) .







lift



get





 // Before map.lift(k) // After map.get(k)
      
      





, ( ), . lift



, ( Map



PartialFunction



) .







get



getOrElse





 //  map.get(k).getOrElse(z) //  map.getOrElse(k, z)
      
      





, . z



, .







Map -



 //  (map: Map[Int, T]) Seq(1, 2, 3).map(map(_)) //  Seq(1, 2, 3).map(map)
      
      





Map[K, V] Function1[K, V], .









 //  map.map(_._1) map.map(_._1).toSet map.map(_._1).toIterator //  map.keys map.keySet map.keysIterator
      
      





( ).









 //  map.map(_._2) map.map(_._2).toIterator //  map.values map.valuesIterator
      
      





( ).







filterKeys





 //  map.filterKeys(p) //  map.filter(p(_._1))
      
      





filterKeys



- . , filterKeys



. , , , , filterKeys(p).groupBy(???)



.







«» ( , ) – , - .







filterKeys



, , , - , . withKeyFilter



( withFilter



).







, .







, filterKeys



( , ), .







, , ( ) , , :







 type MapView[A, +B] = Map[A, B] implicit class MapExt[A, +B](val map: Map[A, B]) extends AnyVal { def withKeyFilter(p: A => Boolean): MapView[A, B] = map.filterKeys(p) }
      
      





MapView



, , view-. :







 def get(k: T) = if (p(k)) map.get(k) else None
      
      





mapValues





 //  map.mapValues(f) //  map.map(f(_._2))
      
      





, . :







 type MapView[A, +B] = Map[A, B] implicit class MapExt[A, +B](val map: Map[A, B]) extends AnyVal { def withValueMapper[C](f: B => C): MapView[A, C] = map.mapValues(f) }
      
      





:







 def get(k: T) = map.get(k).map(f)
      
      







 //  map.filterKeys(!seq.contains(_)) //  map -- seq
      
      





, .









 //  map = map + x -> y map1 = map1 ++ map2 map = map - x map = map -- seq //  map += x -> y map1 ++= map2 map -= x map --= seq
      
      





, , .







8.



Scala , .







:









Scala.







, . , - , . .







翻訳者から



. ( ). firegurafiku , .








All Articles