web-dev-qa-db-ja.com

文字列でのMapとFlatMap

Scalaの関数型プログラミングの原則 からのコレクションの講義を聞いて、私はこの例をみました:

scala> val s = "Hello World"

scala> s.flatMap(c => ("." + c)) // prepend each element with a period
res5: String = .H.e.l.l.o. .W.o.r.l.d

それから、Odersky氏がmapをここで使用しなかった理由に興味がありました。しかし、マップを試してみると、予想とは異なる結果が得られました。

scala> s.map(c => ("." + c))
res8: scala.collection.immutable.IndexedSeq[String] = Vector(.H, .e, .l, .l, .o, 
                                                          ". ", .W, .o, .r, .l, 

私はmap- ingであるため、上記の呼び出しがStringを返すことを期待していました。つまり、「シーケンス」の各アイテムに関数を適用してから、新しい「シーケンス」を返します。

ただし、List[String]に対してmapではなくflatmapを実行できます。

scala> val sList = s.toList
sList: List[Char] = List(H, e, l, l, o,  , W, o, r, l, d)

scala> sList.map(c => "." + c)
res9: List[String] = List(.H, .e, .l, .l, .o, ". ", .W, .o, .r, .l, .d)

なぜIndexedSeq[String]が文字列でmapを呼び出すことの戻り値型であったのですか?

24
Kevin Meredith

この動作の理由は、文字列に「マップ」を適用するために、Scalaは文字列を一連の文字(IndexedSeq[String])。これは、マップ呼び出しの結果として得られるものです。このシーケンスの各要素に対して、操作が適用されます。 Scalaは、文字列をmapを適用するシーケンスとして処理したため、mapreturnsが返されます。

flatMapはその後、そのシーケンスでflattenを呼び出し、その後、それを「変換」して文字列に戻します。

29
fresskoma

また、興味深い「 Scala flatMapの例のコレクション 」があります。最初の例は、flatMapmapの違いを示しています。

scala> val fruits = Seq("Apple", "banana", "orange")
fruits: Seq[Java.lang.String] = List(Apple, banana, orange)

scala> fruits.map(_.toUpperCase)
res0: Seq[Java.lang.String] = List(Apple, BANANA, ORANGE)

scala> fruits.flatMap(_.toUpperCase)
res1: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)

まったく違いますよね?
flatMapStringCharのシーケンスとして扱うため、結果の文字列のリストを文字のシーケンス(Seq[Char])にフラット化します。
flatMapmapflattenの組み合わせであるため、最初にシーケンスでmapを実行し、次にflattenを実行して結果を表示します。

Mapを実行してこれを確認し、それから自分自身をフラット化できます:

scala> val mapResult = fruits.map(_.toUpperCase)
mapResult: Seq[String] = List(Apple, BANANA, ORANGE)

scala> val flattenResult = mapResult.flatten
flattenResult: Seq[Char] = List(A, P, P, L, E, B, A, N, A, N, A, O, R, A, N, G, E)
13
VonC

マップ関数c => ("." + c)はcharを取り、Stringを返します。これは、リストを取得してリストのリストを返すようなものです。 flatMapはそれを平坦化します。

Stringの代わりにcharを返す場合、結果をフラット化する必要はありません。 "abc".map(c => (c + 1).toChar)は「bcd」を返します。

7
ArtemGr

mapを使用すると、文字のリストを取得し、それを文字列のリストに変換できます。それはあなたが見る結果です。 mapはリストの長さを変更しません。文字列のリストには、元の文字列に含まれる文字と同じ数の要素が含まれます。

flatMapを使用すると、文字のリストを取得し、それを文字列のリストに変換し、その後、これらの文字列を単一の文字列にまとめますになります。 flatMapは、リストのリストを作成せずに、リスト内の1つの要素を複数の要素に変換する場合に役立ちます。 (もちろん、これは結果のリストが0を含む任意の長さを持つことができることを意味します。空のリストから始めない限り、mapではこれは不可能です。)

1
kqr

mapに続いてflatternを実行する状況でflatMapを使用します。具体的な状況は次のとおりです。

map(またはfor/yield式)を使用して、既存のコレクションから新しいコレクションを作成します。

•結果のコレクションはリストのリストです。

map(またはfor/yield式)の直後にflattenを呼び出します。

この状況では、代わりにflatMapを使用できます。

例:バッグからすべての整数を追加します

val bag = List("1", "2", "three", "4", "one hundred seventy five")

def toInt(in: String): Option[Int] = {
try {
Some(Integer.parseInt(in.trim))
} catch {
case e: Exception => None
}
}

FlatMapメソッドを使用する

> bag.flatMap(toInt).sum

Mapメソッドの使用(3つのステップが必要)

bag.map(toInt) // List[Option[Int]] = List(Some(1), Some(2), None, Some(4), None)

bag.map(toInt).flatten //List[Int] = List(1, 2, 4)

bag.map(toInt).flatten.sum //Int = 7