Deciding to send this message instead of that one is called control flow – controlling the flow of a computation. Smalltalk offers no special constructs for control flow. Decision logic is expressed by sending messages to booleans, numbers and collections with blocks as arguments.
Conditionals are expressed by sending one of the messages
#ifTrue:
, #ifFalse:
or #ifTrue:ifFalse:
to the result
of a boolean expression:
(17 * 13 > 220) ifTrue: [ 'bigger' ] ifFalse: [ 'smaller' ] ⇒ 'bigger'
The class Boolean
offers a fascinating insight into how much
of the Smalltalk language has been pushed into the class
library. Boolean
is the abstract superclass of the
Singleton classes True
and False
20.
Most of the behaviour of Boolean
instances can be understood
by considering the method ifTrue:ifFalse:
, which takes two
blocks as arguments:
(4 factorial > 20) ifTrue: [ 'bigger' ] ifFalse: [ 'smaller' ] ⇒ 'bigger'
The method is abstract in Boolean
. It is implemented in its
concrete subclasses True
and False
:
True>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock ^ trueAlternativeBlock value False>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock ^ falseAlternativeBlock value
In fact, this is the essence of OOP: when a message is sent to an
object, the object itself determines which method will be used to
respond. In this case an instance of True simply evaluates the true
alternative, while an instance of False evaluates the false
alternative. All the abstract Boolean
methods are implemented
in this way for True
and False
. Look at another
example:
True>>not "Negation----answer false since the receiver is true." ^ false
Booleans offer several useful convenience methods, such as
ifTrue:
, ifFalse:
, ifFalse:ifTrue:
. You also
have the choice between eager and lazy conjunctions and disjunctions:
(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"
In the first example, both Boolean
subexpressions are
evaluated, since &
takes a Boolean
argument. In
the second and third examples, only the first is evaluated, since
and:
expects a Block
as its argument. The
Block
is evaluated only if the first argument is true.
Try to imagine how
and:
andor:
are implemented.
In the Example 5.1 at the beginning of this chapter, there
are 4 control flow #ifTrue:
messages. Each argument is a block of
code and when evaluated, it explicitly returns an expression,
therefore interrupting the method execution.
In the code fragment of Example 5.6 below, we test if a ship is lost in deep space. It depends on two conditions:
#isInOuterSpace
message,
#isGoingOuterSpace
message.
Of course, the condition #2 is only tested when condition #1 is true.
"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]
Loops are typically expressed by sending messages to blocks, integers or collections. Since the exit condition for a loop may be repeatedly evaluated, it should be a block rather than a boolean value. Here is an example of a very procedural loop:
n := 1. [ n < 1000 ] whileTrue: [ n := n * 2 ]. n ⇒ 1024
#whileFalse:
reverses the exit condition:
n := 1. [ n > 1000 ] whileFalse: [ n := n * 2 ]. n ⇒ 1024
You can check all the alternatives in the controlling
method
category of the class BlockClosure
.
#timesRepeat:
offers a simple way to implement a fixed iteration:
n := 1. 10 timesRepeat: [ n := n * 2 ]. n ⇒ 1024
We can also send the message #to:do:
to a number which then acts
as the initial value of a loop counter. The two arguments are the
upper bound, and a block that takes the current value of the loop
counter as its argument:
result := String new. 1 to: 10 do: [:n | result := result, n printString, ' ']. result ⇒ '1 2 3 4 5 6 7 8 9 10 '
You can check all the alternatives in the intervals
method
category of the class Number
.
If the exit condition of method like
whileTrue:
is never satisfied, you may have implemented an infinite loop. Just type Cmd-period to get the Debugger.
A
singleton class is designed to have only one instance. Each of
True
and False
classes has one instance, the values
true
and false
.