web-dev-qa-db-ja.com

コレクションの機能メソッド

私はScalaを学んでいて、コレクションで利用可能なすべてのメソッド(高階関数)に少し戸惑っています。元のコレクションよりも多くの結果を生成するものと、生成するものが少ないもの、そして与えられた問題に最も適切なものは何ですか?私はScalaを勉強していますが、これはほとんどの最新の関数型言語(Clojure、Haskell)とJava 8 Javaコレクション。

具体的には、現在、フィルター付きのマップと折りたたみ/縮小のマップについて疑問に思っています。 foldRight()を使用すると、基になるコレクションを1回トラバースするだけでmap(...)。filter(...)と同じ結果が得られることを嬉しく思います。しかし、友人は、map()が複数のプロセッサによる並列処理に適しているのに対し、foldRight()は順次処理を強制する可能性があると指摘しました。たぶんこれがmapReduce()がとても人気がある理由ですか?

より一般的には、List(List())を取得するために、またはList(List())を渡してList()のみを取得するためにこれらのメソッドのいくつかを一緒にチェーンするとき、私はまだ驚かされることがあります。たとえば、いつ使用しますか:

collection.map(a => a.map(b => ...))

vs.

collection.map(a => ...).map(b => ...)

For/yieldコマンドは、この混乱を防ぐためには何もしません。 「折りたたむ」操作と「展開する」操作の違いについて質問していますか?

あまりにも多くの質問を1つに詰め込もうとしていますか?私はそれを理解すれば、これらすべての質問に答えるか、少なくとも答えを結びつけるという根本的な概念があるのではないかと思います。

6
GlenPeterson

指定された縮小関数としてconsを指定してfoldRight(またはfoldLeft)を使用することにより、マップ/フィルターを大まかにエミュレートできます。たとえば、foldRight(f, L, initial)ここで、_L = [x,y,z]_は次のように展開されます。

f(x, f(y, f(z, initial)))

つまり、f(z, initial)を処理してからf(y, ...)を処理するまで、xを処理できません。 map/filterには存在しない依存関係を作成しています。

マップの順序については...

_(A) collection.map(a => a.map(b => ...))
_

(A)は、コレクションを取得してから、map関数を各要素に適用します。これは、各要素が「マッピング可能な」コレクションであることを意味します。この内部マップは処理されたリスト(リスト)を返すため、collectionの各要素はコレクションのままです。これは、関数をリストのリストの各要素にマップする方法であり、結果は再びリストのリストになります。

_(B) collection.map(a => ...).map(b => ...)
_

(B)リストの各要素を処理し、それらの結果を新しいリストに形成します。次に、このリストは、さらに別のリストを提供する2番目のマップ関数で再度処理されます。

(A)は、リストの内部要素(必要に応じて「サブ要素」)を処理するためのものです。 (B)はリストを複数回処理するためのものです。 (B)を具体的に次のように書くと

_collection.map(a => f(a)).map(b => g(b))
_

それは次と同等であることがわかります

_collection.map(a => g(f(a)))
_

ループのようにそれらを書き出すのに役立つかもしれません。 (A)は埋め込みループを使用しますが、(B)は2つの順次ループを使用します。

これは折り畳みと展開の違いではありません。 (A)も(B)もフォールドではありません。存在するリスト構造は、深くネストされていても保持されます。 Foldはリストからスカラーを作成しますが、展開(それに慣れていない)はスカラーを取り、いくつかのルールに従ってリストを生成します。

[〜#〜] edit [〜#〜]:@GiorgioはflatMapを提案するのが正しかった。これは、マップ上の興味深いバリエーションです。 XのリストをYのリストにマッピングしているとしましょう。そのため、mapに関数_f:X->Y_を渡します。 Xを取りながら複数のYを返す別の計算gがあるとします_g:X->[Y]_。この場合、flatMapを使用します。 mapは、fの結果を取得し、それらをリストに入れます。 flatMapは、gの結果を取得し、それらを連結します。

例リストのリストがあるとしましょう_L = [[1,2,3],[4,5,6]]_

_L.map(a => a.map(b => b * 2))
_

_[[2,4,6],[8,10,12]]_を与えます。しかし、サブリストではなく、1つのリストで各数値を2倍にしたいとします。次に、

_L.flatMap(a => a.map(b => b * 2))
_

これは_[2,4,6,8,10,12]_を与えます。内部関数a => a.map(b => b * 2)がリストを取得して返すことに注意してください。

8
axblount

フォールドは、マップ、リデュース、フィルターよりも一般的です。少し考えてみれば、フォールドの観点からの実装は簡単です。より意図を伝え、起こり得ることを制限するため、最初にそれらを使用することを検討してください。マップは常に同じ長さのリストを生成します。フィルターは値を変更せず、一部を削除する可能性があります。削減すると、リストが単一の値に変換され、3つすべてのフォールドが同時に発生する可能性があります。

リストからリストのリストに移動すると、マップ(またはマップのようなものを実行するフォールド)がどこかにリストを返します。リストからリストへのリストは、どこかでプレイするためにリデュースが入ってくることを意味します。

また、それらに関する議論でスタックオーバーフローについても見ていきます。それぞれのユースケースに役立ちます。

2
stonemetal

入力よりも小さい出力を生成する操作に関して、よくある間違いはmap自体に関係します。直感的には、mapは各入力要素を受け取り、この要素の変換を計算し、結果を出力に配置して、入力と出力が同じサイズになるようにします。

実際、Scalaでは、セット(つまり整数)のmapがセットです。したがって、整数が奇数か偶数かを確立する述語(つまり、ブール関数)を適用すると、結果はブールのセットになります。したがって、入力サイズが何であれ、可能な出力サイズは0、1、2のみです。

val a = Set(1,2,3,4,5)
a.map(_ % 2 == 0)
res0: scala.collection.immutable.Set[Boolean] = Set(false, true)

これはScala規則です。たとえば、Pythonでは、セットのmapは、元のセットと同じ数の要素を含むリストであることに注意してください。

2
mgoeminne