scala - example - Mapa versus FlatMap en String
scala map example (5)
Con el map
estás tomando una lista de caracteres y convirtiéndola en una lista de cadenas. Ese es el resultado que ves. Un map
nunca cambia la longitud de una lista: la lista de cadenas tiene tantos elementos como la cadena original tiene caracteres.
Con flatMap
está tomando una lista de caracteres y convirtiéndola en una lista de cadenas y luego flatMap
a flatMap
en una sola cadena . flatMap
es útil cuando desea convertir un elemento en una lista en varios elementos, sin crear una lista de listas. (Por supuesto, esto también significa que la lista resultante puede tener cualquier longitud, incluido 0, esto no es posible con el map
menos que empiece con la lista vacía).
Al escuchar la lección de Colecciones de los Principios de Programación Funcional en Scala , vi este ejemplo:
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
Entonces, tenía curiosidad de por qué el Sr. Odersky no usaba un map
aquí. Pero, cuando probé el mapa, obtuve un resultado diferente del que esperaba.
scala> s.map(c => ("." + c))
res8: scala.collection.immutable.IndexedSeq[String] = Vector(.H, .e, .l, .l, .o,
". ", .W, .o, .r, .l,
Esperaba que la llamada anterior devuelva una Cadena, ya que estoy mapeando, es decir, aplicando una función a cada elemento en la "secuencia" y luego devolviendo una nueva "secuencia".
Sin embargo, podría realizar un map
lugar de un flatmap
para una List[String]
:
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)
¿Por qué un IndexedSeq[String]
el tipo de retorno del map
de llamada en el String?
La razón de este comportamiento es que, para aplicar el "mapa" a una Cadena, Scala trata la cadena como una secuencia de caracteres ( IndexedSeq[String]
). Esto es lo que se obtiene como resultado de la invocación del mapa, donde se aplica la operación para cada elemento de dicha secuencia. Dado que Scala trató la cadena como una secuencia para aplicar el map
, eso es lo que devuelve el map
.
flatMap
entonces simplemente invoca al flatMap
en esa secuencia luego, lo que luego lo "convierte" nuevamente en una Cadena
Su función de mapa c => ("." + c)
toma un carácter y devuelve un String. Es como tomar una lista y devolver una lista de listas. flatMap aplana esa espalda.
Si devolviera un char en lugar de una cadena, no necesitaría que el resultado se aplanara, por ejemplo, "abc".map(c => (c + 1).toChar)
devuelve "bcd".
También tiene una interesante " colección de ejemplos de Scala flatMap ", el primero de los cuales ilustra esa diferencia entre flatMap
y el 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)
Toda una diferencia, ¿verdad?
ComoflatMap
trata unaString
como una secuencia deChar
, aplana la lista resultante de cadenas en una secuencia de caracteres (Seq[Char]
).
flatMap
es una combinación demap
yflatten
, por lo que primero ejecuta elmap
en la secuencia, luego seflatten
, dando el resultado que se muestra.Puedes ver esto ejecutando el mapa y luego aplanándote:
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)
Use flatMap en situaciones en las que ejecute map seguido de flattern . La situación específica es esta:
• Estás usando un mapa (o una expresión for / yield ) para crear una nueva colección a partir de una colección existente.
• La colección resultante es una lista de listas.
• Llamas a aplanar inmediatamente después del mapa (o una expresión para / rendimiento ).
Cuando estás en esta situación, puedes usar flatMap en su lugar.
Ejemplo: Añadir todos los números enteros de la bolsa
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
}
}
Usando un método flatMap
> bag.flatMap(toInt).sum
Usando el método del mapa (3 pasos necesarios)
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