paradigmas de lenguajes de programación smalltalk (parte ii) 1 er cuatrimestre de 2007
TRANSCRIPT
Paradigmas de Lenguajes de Programación
Smalltalk (Parte II)
1er cuatrimestre de 2007
Anteayer nomás...
Un objeto consta de una representación interna y un protocolo.
En Smalltalk todo es un objeto. Por ejemplo el número 10. La cadena 'hola, soy una cadena'. El valor booleano true. El transcript donde se trabaja. El código del programa que escribo. O sea... ¿todo es un objeto?
¡Sí! Todo, todo.
Anteayer nomás...
También tenemos variables, las cuales pueden referenciar objetos.
Por ejemplo: unaFrase := 'Ser o no ser...'
'Ser o no ser...'String
unaFrase
Anteayer nomás...
Los objetos interactúan entre sí a través del envío de mensajes.
Los mensajes pueden ser unarios, binarios o keyword.
Persona Cafeteraencender
Todo muy lindo, pero...
¿Cómo hago algo útil con todo esto?
Empecemos por un ejercicio fácil. Escribir un fragmento de código que evalúa
la variable x y si es igual a cero imprime ‘Cero’ en el transcript.
Parecería que necesitamos algún tipo de estructura de control condicional if.
Condicionales en Smalltalk
En los lenguajes de programación imperativos existen, como parte del lenguaje, estructuras de control de flujo:
If (condición) {
sentencias
}
¿Pero cómo hacemos esto en Smalltalk donde todo es un objeto?
¿Ideas?
¿Qué tal esta alternativa?
[codigo] executeIf: condicion
No nos sirve. ¿Por qué? Porque el objeto que recibe el mensaje no tiene
forma de verificar si el booleano que recibe es efectivamente true porque necesitaría un if…
Y eso es precisamente lo que estamos intentando implementar.
¿Qué tal esta otra?
condicion ifTrue: [codigo]
Es lo mismo pero ahora el receptor del mensaje es el booleano. ¿Cambia algo?
Sí, porque podemos utilizar el polimorfismo que nos provee este paradigma para desviar el flujo de control.
¿Y cómo hacemos esto?
Booleanos
La condición es un objeto de la clase Boolean
Tiene subclases True y False, cada una con una única constante, true y false respectivamente.
Cada vez que evaluamos una expresión booleana en Smalltalk el resultado es una de estas dos constantes.
Boolean
True False
condicion ifTrue: [sentencias].
¿Cuál es el parámetro? Un bloque de código, perteneciente a la clase
BlockClosure. (Recordar: ¡Todo es un objeto!) Los bloques pueden tener parámetros o no:
[Transcript show: ‘Hola’.] [:x :y | x == 10 + y.]
Se ejecutan enviándoles el mensaje value, que puede ser unario para el caso de bloques sin parámetros o keyword con tantos parámetros como el bloque tiene.
Bloques
Ejecutando (evaluando) bloques: [Transcript show: ‘Hola’.] value.
Imprime la cadena ‘Hola’ en el transcript. [:x :y | x == 10 + y.] value: 21 value: 11.
Devuelve true.
Los bloques pueden tener variables locales: miBloque := [:i | |z| z := i. z := z * z. z + 1.]
Devuelven el resultado de la última sentencia: miBloque value: 2. devuelve 5
¿Cómo está implementado el if?
Recordar, Boolean tiene dos subclases. Clase True, método ifTrue: unBloque
Ejecuto el bloque: unBloque value.
Clase False, método ifTrue: unBloque No hago nada.
¿Entoces el ejercicio…?
Escribir un fragmento de código que evalúa la variable x y si es igual a cero imprime ‘Cero’ en el transcript.
x == 0 ifTrue: [Transcript show: ‘Cero‘.].
Más de boolean
condicion ifFalse: [sentencias] condicion ifTrue: [sentencias1]
ifFalse: [sentencias2] booleano1 & booleano2
booleano1 | booleano2
booleano and: [sentencias] Evalua el bloque y al resultado le aplica and con el booleano.
booleano or: [sentencias] Evalua el bloque y al resultado le aplica or con el booleano.
Otro ejercicio
Escribir un bloque de código que tome un parámetro y al ser evaluado (en un entero positivo) imprima en el transcript todos los números desde el 0 hasta el que recibió.
Parece que ahora necesitamos algún tipo de estructura de control que nos permita iterar.
m to: n do: [:actual | sentencias]
La clase Integer acepta un mensaje to:do: Espera otro entero y un bloque con un parámetro. Evalúa el bloque usando como parámetro cada entero entre m
y n. ¿Entonces el ejercicio…?
[:limite |
0 to: limite do:
[:actual |
Transcript show: actual displayString ; cr.
].
]
Otras formas de iterar
m to: n by: step do: [:actual | sentencias]
Igual pero incrementando de a step en cada paso. m timesRepeat: [sentencias]
Repite m veces el bloque sentencias. [condicion] whileTrue: [sentencias]
Ejecuta sentencias mientras la evaluación de condicion sea true. ¿Por qué no usar un Boolean?
[condicion] whileFalse: [sentencias]
Análoga a la anterior.
Igualdad
Smalltalk puede verificar dos tipos de igualdades entre objetos.
Igualdad estricta: a = b es true si a y b son la misma instancia.
Igualdad semántica (o equivalencia): a == b es true si a y b son equivalentes según alguna
relación que debe ser implementada para cada clase.
a ~= b sería (a = b) not a ~~ b sería (a == b) not
Colecciones en Smalltalk
Smalltalk ofrece una amplia variedad de tipos de colecciones que podemos usar según la necesidad del caso:
¿Elementos ordenados? ¿Se admiten duplicados? Etc.
Tener en cuenta que las colecciones son heterogéneas, siempre se admiten elementos de tipos diversos.
Colecciones, colecciones, …
Algunas operaciones básicas (I)
Array
•at:•at:put:•size
a := #(1 2 3 4) copy.a at: 1.a at: 2 put: ‘dos’.a size
Set
•add:•size•includes:
s := Set new.s add: 1; add: 1; add: 2.s size.s includes: 4.
Bag
•add:•size•includes:•occurrencesOf:
b := Bag new.b add: 1; add: 1; add: 2.b size.b includes: 1.b occurrencesOf: 1.
Dictionary
•at:put:•at:•includes: (contiene valor)•includesKey: (contiene clave)•size•keys
d := Dictionary new.d at: 'hola' put: 'hello'.d at: ‘hola'.d includesKey: 'loro'.
Algunas operaciones básicas (II)
Interval •includes:
in := Interval from: 3 to: 30 by: 5.
in includes: 4.
in includes: 8.
OrderedCollection
•add:•at:•at:put:•first•last•addFirst:•addLast:•removeFirst:•removeLast:•indexOf:
oC := OrderedCollection new.
oC add: 3; add: 2; add: 5.
oC add: 4 after: 2.
SortedCollection
•add:•sortBlock:
x := SortedCollection new.
x add: 3; add: 2; add: 5.
x sortBlock: [:a :b | a >= b].
Operaciones para todas las collections (I)
do: [:actual | sentencias]Evalúa el bloque en cada uno de los elementos de la colección.
select: [:actual | sentencias]
El bloque debe retornar un valor booleano. Se devuelve otra colección (del mismo tipo de la receptora) conteniendo únicamente los elementos para los cuales la ejecución del bloque da true.
reject: [:actual | sentencias]Análoga a la anterior pero guardando los elementos que hacen al bloque false.
collect: [:actual | sentencias]Se devuelve otra colección (del mismo tipo de la receptora) conteniendo los resultados de la aplicación del bloque a cada elemento de la colección receptora.
Operaciones para todas las collections (II)
detect: [:actual | sentencias]
El bloque debe retornar un valor booleano. Se devuelve el primer elemento de la colección que hace al bloque evaluar en true.
En caso de que todos evalúen el bloque en false se genera un error en tiempo de ejecución.
detect: [:actual | sentencias1] ifNone: [sentencias2]
Igual a la anterior, pero en caso de no encontrar ningún elemento que haga al bloque ser verdadero, se evalúa el segundo bloque y no se genera un error.
inject: casoBase into: [:resParcial :elemActual | sentencias]
El primer parámetro del bloque es el resultado parcial de la operación, comenzando por casoBase. El segundo parámetro es el elemento actual de la colección receptora.
Se devuelve el resultado de la última evaluación
Ejercicio de colecciones
Agregarle a la clase Collection un método:selectAnyTwoOf: bloque1 or: bloque2 or: bloque3
Donde los tres bloques tienen un parámetro y evaúan a Boolean.
Debe devolver una nueva colección (del mismo tipo de la receptora) cuyos elementos son aquellos de la colección original tales que hacen valer true a exactamente dos de los bloques.
Ejercicio de colecciones: solución
selectAnyTwoOf: bloque1 or: bloque2 or: bloque3
^self select:
[:elem |
|n|
n := 0.
(bloque1 value: elem) ifTrue: [n := n + 1.].
(bloque2 value: elem) ifTrue: [n := n + 1.].
(bloque3 value: elem) ifTrue: [n := n + 1.].
n == 2.
].
Ejercicio de parcial
Agregar en Smalltalk a la clase Collection el siguiente método:
generateBlock: aBlockList
Este método recibe una lista de bloques de un parámetro que evalúan a Boolean.
Se debe devolver un nuevo bloque b de un parámetro que evalúa a Boolean tal que:
b value:n es verdadero si y sólo si: b1 value:n es True b2 value:n es False b3 value:n es True b4 value:n es False
… (valores de verdad intercalados)
donde <b1,b2,...,bn> son todos los bloques de aBlockList que son satisfechos por al menos un elemento de la colección receptora.
Ejercicio parcial: solución
generateBlock: aBlockSecuence
|secuenciaFiltrada alter|
secuenciaFiltrada := aBlockSecuence select:
[:b | (self select: [:e | b value:e] ) size > 0].
alter := false.
^ secuenciaFiltrada inject: [:p | true] into:
[:b :m |
(alter := (alter not)).
alter ifTrue:
[ [:p | (b value: p) & ((m value: p) == true)] ]
ifFalse:
[ [:p | (b value: p) & ((m value: p) == false)] ]
].
¡Eso es todo!
¿?clase avisar: ‘eso es todo por hoy’