Scala WAT:コレクション

前回 、オプションの値の処理を考え出したとき 、Scalaライブラリのエレガントなツールを誤って使用すると、引き続き自分の足で撃つことができることがわかりました。



今回はコレクションに移りましょう。 Scalaコレクション標準ライブラリは非常に豊富であるため、最も要求の厳しい開発者でさえ、既製のメソッドを提供できます。 どの場合にどの方法を適用するかは通常説明されておらず、すべてが経験から学習されます。 通常、最初は誰でもfilter



map



を認識しmap



、これはこれに限定できます。特定のファンタジーでは、これらの関数でのみ多くのアルゴリズムを実装できるためです。 ただし、これらの方法はドメインに対して最適ではないか、不可能な結果を​​もたらす可能性がありますが、処理する必要があります。



以下では、標準ライブラリのどの機能が誤って頻繁に使用され、何が改善できるかについて説明します。



foreachおよびfor(val i = 0; i <n; i ++)



Scalaに切り替えると、 for(;;)



がないために多くの人が別れます。 誰か、特にCで書いた人は、そのようなサイクルがなければプログラムを書くことはまったく不可能であり、より高度なものは外部コードの初期条件とサイクルのステップを削除しwhile



アナログを書き込もうとすると考えています:



 var i = 0 //  var! while(i < n) { //  - ,   i i = i+1 }
      
      





実際、 for(val i = 0; i< n; i++)



の最も正確な類似物はfor(i <- 0 until n)





すべてがここに表示されていましたが、すぐに表示されるとは限りません。 正しく記述するには、Rangeクラスの操作を理解し、 Code Commitなどのブログでの使用例を参照する必要があります。



イテレータ



多くの場合、古いJavaコードで作業する場合、通常は反復できないクラスが到着します。 java.sql.ResultSet



を例として取り上げjava.sql.ResultSet



Enumeration



もロールします)。 Iterableではありません。つまり、JavaConversionsへの透過的な変換はないため、命令型で排他的に操作するか、中間の可変コレクションのコンテンツにコピーする必要があります。オプションとして、ビルダーを介して不変ビルドします。 怖い。



この場合、Scala標準ライブラリには、少なくともTraversable



であるResultSet



から食用のIterator



を作成できるIteratorクラスがあります。



 val resultSetIterator = Iterator.continually(resultSet).takeWhile(_.next())
      
      





単純な置換とバイナリロジック



呼び出しチェーンをより単純な、または1つの単純なメソッドで置き換えることができる場合、単純な置換ルールから始めましょう。 あなたを助けることができる最も簡単な方法は、 forall



find



です。 私はよく観察しました:



 option.map(myValueIsGoodEnough).getOrElse(false)
      
      





代わりに

 option.exists(goodEnoughValue) //   `option exists goodEnoughValue`
      
      





それから

 iterable.filter(goodEnough).headOption
      
      





代わりに

 iterable.find(goodEnough)
      
      





それも起こりました(ただし、コード内で!)

 iterable.foldLeft(true)(_ && goodEnough)
      
      





の代わりに

 iterable.forall(goodEnough)
      
      





言うまでもなく、シンプルなオプションは読みやすいだけでなく、より効率的に実装されます。これは、 map



filter



foldLeft



がコレクションの長さに関係なくコレクション全体をチェックするためです。適切な(または不適切な)要素が見つかるとすぐに終了し、追加の中間コレクションは生成されません。



Coursera.comScalaコースにはspan



dropWhile



便利な機能もいくつかdropWhile



ています。 それらを使用することは実際には明らかではないかもしれません。なぜなら、 filter



はこれらのケースでもうまく機能しますが、あまり効率的ではありません。なぜなら、彼はいつでも要素をチェックする必要がないことを知らないからです:それらはすべてすでに適合しているか適合していないからです。 たとえば、最近まで、私は自分自身が、 filter



、およびdropWhile



takeWhile



内ではなく、間隔内にない一連の為替レートの開始と終了をクリーンdropWhile



takeWhile







集計



fold



foldLeft



およびfoldRight



非常に強力であり、完全に異なる問題を解決するために使用できます。

たとえば、 foldLeft



について説明します。その助けを借りて、コレクションの要素の合計を取得できます。



 iterable.foldLeft(0) { (accum, elem) => accum + elem }
      
      





このようなコードの標準的なスペルは次のとおりです。



 iterable.foldLeft(0) { _ + _ }
      
      





これに対するMartin Oderskyは、Scala Daysで興味深いオプションを示しました。



 (0 /: iterable)(_ + _)
      
      





ドミノが左から右に落ちるかのように言う:)プラスはここでより詳細に説明されていますが、主題領域でよく知られていない場合、私はまだ記号演算子に懐疑的です。



いずれにせよ、このような状況では、このコードを記述しないでください。タイプクラスNumeric



要素を持つコレクションには特別なメソッドがあるため、より簡単に記述できます。



 iterable.sum
      
      





min



max



max



機能も利用できます(専門職人はコレクションを並べ替えて頭/尾をとるのが好きです。私はこれをよく見ていません)。



高レベルアルゴリズムの手順を次々に適用するための折りたたみ方法を保存することをお勧めします。たとえば、責任の連鎖、またはmap-reduceでreduceを実行します。



時折グラフや一般的な情報を処理するときに、要素をペア(現在と前)で処理しなければならない場合があります。この場合、本当にしたいかもしれません。



 for(i <- 1 until seq.size) { result ++= combine(seq(i-1), seq(i)) }
      
      





Scalaでは、1つの要素だけシフトされたシーケンスを縫い付けてから、両方の要素を処理する関数を適用できます。



 seq.zip(seq.drop(1)).map(combine) //    list.zip(list.tail).map(combine) // ,    
      
      





シートが空でないことを必ず確認してください!



同じコードを記述する方がより正確です



 seq.sliding(2).map(calculateDelta)
      
      







そして今、良いニュースです。JavaOneで、これらの単純化の多くをIntelliJのメンバーに登録することが判明しました。ScalaDaysによって既にそれらを実装することができました。







現在、Scalaの検査に関与している特別な人がいるので、単純化に関して興味深い考えがあれば、 チケットを作成してください。



良いコードを書いて幸せになりましょう。



All Articles