La decisión de enviar este mensaje en lugar de aquél se denomina control de flujo, es decir, controlar el flujo de un cálculo. Smalltalk no ofrece construcciones especiales para el control de flujo. La lógica de decisión se expresa enviando mensajes a booleanos, números y colecciones con bloques como argumentos.
Las condiciones se expresan enviando uno de los mensajes #ifTrue:,
#ifFalse: o #ifTrue:ifFalse: al resultado de una expresión
booleana:
(17 * 13 > 220) ifTrue: [ 'bigger' ] ifFalse: [ 'smaller' ] ⇒ 'bigger'
La clase Boolean ofrece una visión fascinante de cuánto del
lenguaje Smalltalk se ha incorporado a la biblioteca de
clases. Boolean es la superclase abstracta de las clases
Singleton True y False20.
La mayor parte del comportamiento de las instancias Boolean
puede entenderse considerando el método ifTrue:ifFalse:, que
toma dos bloques como argumentos:
(4 factorial > 20) ifTrue: [ 'bigger' ] ifFalse: [ 'smaller' ] ⇒ 'bigger'
El método es abstracto en Boolean. Se implementa en sus
subclases concretas True y False:
True>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock ^ trueAlternativeBlock value False>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock ^ falseAlternativeBlock value
Ejemplo 5.4: Implementaciones de ifTrue:ifFalse:
De hecho, esta es la esencia de la POO: cuando se envía un mensaje a un
objeto, el propio objeto determina qué método se utilizará para
responder. En este caso, una instancia de True simplemente
evalúa la alternativa verdadera, mientras que una instancia de False
evalúa la alternativa falsa. Todos los métodos abstractos
Boolean se implementan de esta manera para True y
False. Veamos otro ejemplo:
True>>not "Negation----answer false since the receiver is true." ^ false
Ejemplo 5.5: Implementación de la negación
Los booleanos ofrecen varios métodos útiles, como ifTrue:,
ifFalse:, ifFalse:ifTrue:. También puedes elegir entre
conjunciones y disyunciones rápidas y perezosas:
(1 > 2) & (3 < 4) ⇒ false "must evaluate both sides" (1 > 2) and: [ 3 < 4 ] ⇒ false "only evaluate receiver" (1 > 2) and: [ (1 / 0) > 0 ] ⇒ false "argument block is never evaluated, so no exception"
En el primer ejemplo, se evalúan ambas subexpresiones Boolean,
ya que & toma un argumento Boolean. En el segundo y
tercer ejemplo, solo se evalúa la primera, ya que and: espera
un Block como argumento. El Block solo se evalúa si el
primer argumento es verdadero.
Intenta imaginar cómo están implementados
and:yor:.
Ejercicio 5.2: Implementar and: y or:
En el Ejemplo 5.1 al principio de este capítulo, hay 4
mensajes de flujo de control #ifTrue:. Cada argumento es un
bloque de código y, cuando se evalúa, devuelve explícitamente una
expresión, interrumpiendo así la ejecución del método.
En el fragmento de código del Ejemplo 5.6 a continuación, comprobamos si una nave se ha perdido en el espacio profundo. Depende de dos condiciones:
#isInOuterSpace,
#isGoingOuterSpace.
Por supuesto, la condición n.º 2 solo se comprueba cuando la condición n.º 1 es verdadera.
"Are we out of screen?
If so we move the mobile to the other corner
and slow it down by a factor of 2"
(self isInOuterSpace and: [self isGoingOuterSpace])
ifTrue: [
velocity := velocity / 2.
self morphPosition: self morphPosition negated]
Ejemplo 5.6: Nave perdida en el espacio
Los bucles suelen expresarse enviando mensajes a bloques, enteros o colecciones. Dado que la condición de salida de un bucle puede evaluarse repetidamente, debe ser un bloque en lugar de un valor booleano. A continuación se muestra un ejemplo de un bucle muy procedimental:
n := 1. [ n < 1000 ] whileTrue: [ n := n * 2 ]. n ⇒ 1024
#whileFalse: invierte la condición de salida:
n := 1. [ n > 1000 ] whileFalse: [ n := n * 2 ]. n ⇒ 1024
Puedes consultar todas las alternativas en la categoría de métodos de
control de la clase BlockClosure.
#timesRepeat: ofrece una manera sencilla de implementar una
iteración:
n := 1. 10 timesRepeat: [ n := n * 2 ]. n ⇒ 1024
También podemos enviar el mensaje #to:do: a un número que luego
actúa como el valor inicial de un contador de bucle. Los dos argumentos
son el límite superior y un bloque que toma el valor actual del contador
de bucle como su argumento:
result := String new. 1 to: 10 do: [:n | result := result, n printString, ' ']. result ⇒ '1 2 3 4 5 6 7 8 9 10 '
Puedes consultar todas las alternativas en la categoría del método
intervals de la clase Number.
Si la condición de salida de un método como
whileTrue:nunca se cumple, es posible que hayas implementado un bucle infinito. Solo tienes que pulsar Cmd-punto para acceder al depurador.
Una clase
singleton está destinada a tener sólo una instancia. Cada una de
las clases True y False tiene sólo una instancia, los
valores true y false.