Este es un pequeño resumen de mensajes que entiende una colección. Lo que sigue sirve para todos los sabores de colecciones.
¿Qué puedo hacer con los conjuntos de objetos? Muchas cosas, las colecciones entienden mensajes muy útiles para ayudarnos a abstraernos un poquito. Vamos a separarlos en los que tienen efecto colateral sobre la colección receptora y los que no.
Estos mensajes alteran a la colección que recibe el mensaje. Si no quiero perder la colección original puedo o bien copiar la colección mandándole shallowCopy y luego usar estos mensajes, o replantearme si no quería usar alguno de los mensajes de colecciones sin efecto :)
Smalltalk
pajaros add: pepita.
Wollok
pajaros.add(pepita)
También existe el mensaje addAll: en Smalltalk y addAll(conjunto) en Wollok que recibe otra colección y agrega todos los elementos del parámetro a la colección receptora.
Smalltalk
pajaros addAll: picaflores
Wollok
pajaros.addAll(picaflores)
Smalltalk
pajaros remove: pepita.
Wollok
pajaros.remove(pepita)
Análogamente al addAll:/addAll(conjunto), existe el removeAll:/removeAll(conjunto) que le quita a la receptora todos los que estén en la colección parametrizada.
Estos mensajes se pueden mandar tantas veces como queramos sin miedo de alterar la colección receptora.
Smalltalk
pajaros size.
Wollok
pajaros.size()
¿Que me devuelve? Un objeto número con la cantidad de objetos que conoce.
Smalltalk
pajaros includes: pepita.
Wollok
pajaros.contains(pepita)
¿Que me devuelve? Un objeto booleano, true o false
Smalltalk
golondrinas union: picaflores.
Wollok
golondrinas.union(picaflores)
¿Que me devuelve? Una nueva colección con la unión de ambas.
Smalltalk
aves intersection: voladores.
Wollok
aves.intersection(voladores)
¿Que me devuelve? Una nueva colección con la intersección de ambas.
Siendo que la unión y la intersección está pensada para conjuntos matemáticos, estos mensajes sólo lo entienden los sets.
Además en Wollok puede hacerse:
golondrinas + picaflores
Que retorna la concatenación de ambos conjuntos, retornando una colección del mismo sabor que la receptora.
Smalltalk
pajaros select: [:unPajaro | unPajaro estaDebil ].
usuarios select: [:unUsuario | unUsuario deuda > 1000].
Wollok
pajaros.filter({unPajaro => unPajaro.estaDebil()})
usuarios.filter({unUsuario => unUsuario.deuda() > 1000})
¿Que me devuelve? Una nueva colección con los objetos que cuando se los evalúa con el bloque, dan true.
Smalltalk
pajaros detect: [:unPajaro | unPajaro estaDebil ].
Wollok
pajaros.find({unPajaro => unPajaro.estaDebil()})
¿Que me devuelve? Un objeto de la colección que cuando se lo evalúe con el bloque, de true.
¿Qué pasa si no hay ningún objeto que cumpla la condición? Explota, lo cual tiene sentido porque no sabe qué hacer si no lo tiene. Por eso existe otra versión en la cual podemos decirle qué devolvernos si no hay ninguno.
Smalltalk
pajaros detect: [:unPajaro | unPajaro estaDebil ] ifNone: [ pajaros anyOne ].
Wollok
pajaros.findOrElse({unPajaro => unPajaro.estaDebil()}, { pajaros.anyOne() })
Smalltalk
pajaros collect: [:unPajaro | unPajaro ultimoLugarDondeFue].
Wollok
pajaros.map({unPajaro => unPajaro.ultimoLugarDondeFue()})
¿Que me devuelve? Una nueva colección con los objetos que devuelve el bloque al aplicarlo con cada elemento.
Smalltalk
pajaros allSatisfy: [:unPajaro | unPajaro estaDebil].
Wollok
pajaros.all({unPajaro => unPajaro.estaDebil()})
¿Que me devuelve? un booleano que indique si todos los objetos de la colección dan true al evaluarlos con el bloque.
Smalltalk
pajaros anySatisfy: [:unPajaro | unPajaro estaDebil].
Wollok
pajaros.any({unPajaro => unPajaro.estaDebil()})
¿Que me devuelve? un booleano que indique si alguno de los objetos de la colección da true al evaluarlo con el bloque.
Si queremos evaluar un bloque de dos parámetros con cada elemento de la colección, usando como primer parámetro la evaluación previa, y como segundo parámetro ese elemento (o sea, lo que en funcional vimos como fold)
Smalltalk
pajaros inject: 0 into: [:inicial :unPajaro | inicial + unPajaro peso]. "sumatoria de pesos"
pajaros inject: (pajaros anyOne) into: [:masFuerte :unPajaro |
(unPajaro energia < masFuerte energia) ifTrue: [unPajaro] ifFalse: [masFuerte]] "maximo segun energia"
Wollok
pajaros.fold(0, {inicial, unPajaro => inicial + unPajaro.peso()}) // sumatoria de pesos
pajaros.fold(pajaros.anyOne(), {masFuerte, unPajaro =>
if(unPajaro.energia() < masFuerte.energia()) unPajaro else masFuerte }) // maximo segun energia
¿Que me devuelve? La ultima evaluación del bloque.
¿Para qué me sirve? Para muchas cosas: obtener el que maximice o minimice algo, obtener el resultado de una operación sobre todos (p.ej. sumatoria), y más. Por lo general existen operaciones de más alto nivel para la mayoría de las reducciones más comunes, de ser así obviamente es preferible usar esos mensajes en vez de hacer la reducción.
Por ejemplo, en Wollok podríamos haber hecho directamente:
pajaros.sum({unPajaro => unPajaro.peso()})
pajaros.max({unPajaro => unPajaro.energia()})
En Smalltalk existen los mensajes asSet, asBag, asOrderedCollection y asSortedCollection: que retornan una colección nueva a partir de la receptora que tiene otras características (ver sabores de colecciones). En particular asSet y asSortedCollection: son de uso más común para quitar repetidos u ordenar en base a un criterio respectivamente.
usuarios.asSortedCollection: [:usuarioJoven :usuarioViejo | usuarioJoven edad < usuarioViejo edad ]
En Wollok, al sólo haber sets y listas, lo que ambas entienden son asSet()
y asList()
. Las listas ordenadas por un criterio pueden obtenerse mediante el mensaje sortedBy(criterio)
, que retorna una nueva colección con los elementos de la receptora ordenadas según el bloque recibido.
usuarios.sortedBy({ usuarioJoven, usuarioViejo => usuarioJoven.edad() < usuarioViejo.edad() })
A su vez en Wollok también existe el mensaje sortBy(criterio)
que se diferencia en que en vez de retornar una nueva colección ordenada de esa forma, modifica la colección receptora para quedar ordenada así. O sea, produce un efecto colateral.
El mensaje do: en Smalltalk o forEach(bloque) en Wollok, sirve para hacer algo con cada objeto de la colección. Este mensaje en sí mismo no tiene efecto colateral, pero tampoco tiene un valor de retorno que pueda interesarnos. Entonces, ¿cuándo se usa? Sólo tiene sentido usar este mensaje cuando lo que queremos hacer sobre cada elemento (o sea, el bloque) sí produce un efecto.
Smalltalk
pajaros do: [:unPajaro | unPajaro vola: 100 ].
Wollok
pajaros.forEach({unPajaro => unPajaro.vola(100)})
Yo no pretendo recolectar resultados, sólo quiero que pasen cosas con cada elemento.
Importante: no usar el do:/forEach para todo, los mensajes que vimos anteriormente son abtracciones mucho más ricas que nos permiten concentrarnos en nuestro objetivo y no en el algoritmo para lograr el resultado, seamos declarativos!