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
を呼び出すことの戻り値型であったのですか?
この動作の理由は、文字列に「マップ」を適用するために、Scalaは文字列を一連の文字(IndexedSeq[String]
)。これは、マップ呼び出しの結果として得られるものです。このシーケンスの各要素に対して、操作が適用されます。 Scalaは、文字列をmap
を適用するシーケンスとして処理したため、map
returnsが返されます。
flatMap
はその後、そのシーケンスでflatten
を呼び出し、その後、それを「変換」して文字列に戻します。
また、興味深い「 Scala flatMapの例のコレクション 」があります。最初の例は、flatMap
とmap
の違いを示しています。
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)
まったく違いますよね?
flatMap
はString
をChar
のシーケンスとして扱うため、結果の文字列のリストを文字のシーケンス(Seq[Char]
)にフラット化します。flatMap
はmap
とflatten
の組み合わせであるため、最初にシーケンスで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)
マップ関数c => ("." + c)
はcharを取り、Stringを返します。これは、リストを取得してリストのリストを返すようなものです。 flatMapはそれを平坦化します。
Stringの代わりにcharを返す場合、結果をフラット化する必要はありません。 "abc".map(c => (c + 1).toChar)
は「bcd」を返します。
map
を使用すると、文字のリストを取得し、それを文字列のリストに変換できます。それはあなたが見る結果です。 map
はリストの長さを変更しません。文字列のリストには、元の文字列に含まれる文字と同じ数の要素が含まれます。
flatMap
を使用すると、文字のリストを取得し、それを文字列のリストに変換し、その後、これらの文字列を単一の文字列にまとめますになります。 flatMap
は、リストのリストを作成せずに、リスト内の1つの要素を複数の要素に変換する場合に役立ちます。 (もちろん、これは結果のリストが0を含む任意の長さを持つことができることを意味します。空のリストから始めない限り、map
ではこれは不可能です。)
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