proceso - ¿Cómo funciona la identidad grupal de Scala?
identidad personal y colectiva (5)
Echa un vistazo a
str.groupBy(identity)
que devuelve
scala.collection.immutable.Map[Char,String] = Map(b -> bbb, d -> dd, a -> aaa, c -> cccc)
así que la clave por la cual los elementos se agrupan es el carácter.
Estaba explorando y encontré una pregunta sobre cómo agrupar una String
por sus caracteres, como este:
La entrada:
"aaabbbccccdd"
Produciría la siguiente salida:
"aaa"
"bbb"
"cccc"
"ddd"
y encontré esta sugerencia:
val str = "aaabbbccccdd"[
val list = str.groupBy(identity).toList.sortBy(_._1).map(_._2)
Y este compañero de identity
me dio curiosidad. Descubrí que está definido en PreDef
esta manera:
identity[A](x: A): A
Así que básicamente devuelve lo que se le da, ¿verdad? ¿Pero cómo se aplica eso en la llamada a groupBy
?
Lo siento si esta es una pregunta básica, es solo que la programación funcional todavía está enredando un poco mi cerebro. Déjeme saber si hay alguna información que pueda proporcionar para aclarar esta pregunta.
Esta es tu expresión:
val list = str.groupBy(identity).toList.sortBy(_._1).map(_._2)
Vayamos artículo por función por función. El primero es groupBy, que particionará su String usando la lista de claves pasadas por la función discriminadora, que en su caso es identidad. La función discriminadora se aplicará a cada carácter en la pantalla y todos los caracteres que devuelven el mismo resultado se agruparán. Si queremos separar la letra a del resto, podríamos usar x => x == ''a''
como nuestra función discriminadora. Eso agruparía sus caracteres de cadena en el retorno de esta función (verdadero o falso) en el mapa:
Map(false -> bbbccccdd, true -> aaa)
Al utilizar la identity
, que es una forma "bonita" de decir x => x
, obtenemos un mapa donde cada personaje se separa en el mapa, en su caso:
Map(c -> cccc, a -> aaa, d -> dd, b -> bbb)
Luego convertimos el mapa en una lista de tuplas (char,String)
con toList
.
sortBy
por char con sortBy
y solo mantenga la cadena con el map
obteniendo su resultado final.
Para entender esto, simplemente llame a -Xprint:typer
repl con la opción -Xprint:typer
:
val res2: immutable.Map[Char,String] = augmentString(str).groupBy[Char]({
((x: Char) => identity[Char](x))
});
Scalac convierte una String
simple en StringOps
con una subclase de TraversableLike
que tiene un método groupBy
:
def groupBy[K](f: A => K): immutable.Map[K, Repr] = {
val m = mutable.Map.empty[K, Builder[A, Repr]]
for (elem <- this) {
val key = f(elem)
val bldr = m.getOrElseUpdate(key, newBuilder)
bldr += elem
}
val b = immutable.Map.newBuilder[K, Repr]
for ((k, v) <- m)
b += ((k, v.result))
b.result
}
Por lo tanto, groupBy contiene un mapa en el que los caracteres de inserción regresan por función de identidad.
Primero, veamos qué sucede cuando se itera sobre una cadena:
scala> "asdf".toList
res1: List[Char] = List(a, s, d, f)
A continuación, considere que a veces queremos agrupar elementos sobre la base de algún atributo específico de un objeto.
Por ejemplo, podríamos agrupar una lista de cadenas por longitud como en ...
List("aa", "bbb", "bb", "bbb").groupBy(_.length)
¿Qué pasa si solo quería agrupar cada elemento por el propio elemento. Podrías pasar la función de identidad así:
List("aa", "bbb", "bb", "bbb").groupBy(identity)
Podrías hacer algo tonto como esto, pero sería tonto:
List("aa", "bbb", "bb", "bbb").groupBy(_.toString)
Siempre que intentes usar métodos como groupBy
en el String. Es importante tener en cuenta que se convierte implícitamente a StringOps
y no a List[Char]
.
StringOps
La firma de groupBy
está dada por:
def groupBy[K](f: (Char) ⇒ K): Map[K, String]
Por lo tanto, el resultado está en la forma -
Map[Char,String]
Lista [Char]
La firma de groupBy
está dada por:
def groupBy[K](f: (Char) ⇒ K): Map[K, List[Char]]
Si se hubiera convertido implícitamente a List[Char]
el resultado sería de la forma:
Map[Char,List[Char]]
Ahora, esto debería responder implícitamente a su pregunta curiosa, ya que groupBy
descubrió cómo groupBy
en Char
(ver la firma) y, a la vez, darle Map[Char, String]
.