Chapter 4
Decisions and loops
A decision in Ether is a word that takes lists and runs one of them. A loop
is a word that runs a list again and again. Neither needs any new syntax: the
lists are ordinary { } lists.
Comparing
equals, less-than and greater-than push
true or false. As with -, the value on
top is the subject: a b less-than asks whether
b is less than a.
| program | asks |
|---|---|
x # 10 less-than | is 10 less than x? |
# 10 x less-than | is x less than 10? |
x # 10 greater-than | is 10 greater than x? |
x # 10 equals | is x equal to 10? |
# 7 = x # 10 x less-than debug x # 10 less-than debug
true false values: ( )
This order is the most common source of mistakes in Ether. When a
comparison gives the opposite of what you meant, swap its two operands.
not, and and or combine booleans.
Choosing
if takes a boolean and a list, and runs the list when the
boolean is true. unless runs it when it is
false. either takes a boolean and two lists, and runs
the first list for true and the second for false.
# 7 = x
# 10 x less-than { " small " debug } if
# 10 x less-than { " small " } { " big " } either debug
small small values: ( )
Loops
while takes two lists: a condition and a body. It runs the
condition; if that leaves true, it runs the body and starts again.
until is the same but stops when the condition is
true.
# 1 = i
{ # 4 i less-than } { i debug i ++ = i } while
1 2 3 values: ( )
foreach takes a list and a body, and runs the body once for each
element, top first, with the element pushed on the values stack.
[ # 1 # 2 # 3 ] { debug } foreach
# 0 [ # 1 # 2 # 3 ] { + } foreach debug
1 2 3 6 values: ( )
The second line keeps a running total under the elements: each turn adds
the element to it. A body does not have to leave anything, or it can leave
something only sometimes. Inside [ ], whatever the turns leave
becomes a list, which is how to keep some elements and drop others:
[ [ # 1 # 2 # 3 # 4 # 5 # 6 ] { dup # 2 swap % # 0 equals { } { del } either } foreach ]
values: ( ( 2 4 6 ) )
That kept the even numbers: # 2 swap % is the remainder after
dividing by 2. map is like foreach, but each turn's
top value goes into a new list for you. If a turn leaves more than one value,
the others stay on the values stack.
[ # 1 # 2 # 3 ] { dup * } map debug
( 1 4 9 ) values: ( )
loop runs its list forever. terminate ends the
program from anywhere.
# 0 = i
{ i ++ = i i debug i # 3 equals { terminate } if } loop
1 2 3
Recursion
Because a word looks names up when it runs, a word can use itself. A
factorial: if n is less than 2 the answer is 1, otherwise it is
n times the factorial of n − 1.
{ dup # 2 swap less-than
{ del # 1 }
{ dup -- factorial * }
either } => factorial
# 6 factorial debug
720 values: ( )
Exercise 4.1
Bind n to 150. Print big if n is
greater than 100, and small otherwise.
A correct answer prints:
big values: ( )
One answer
# 150 = n
n # 100 less-than { " big " } { " small " } either debug
big values: ( )
Exercise 4.2
Count down: print 5, 4, 3, 2, 1, each on its own line, using
while.
A correct answer prints:
5 4 3 2 1 values: ( )
One answer
# 5 = n
{ n # 0 less-than } { n debug n -- = n } while
5 4 3 2 1 values: ( )
Exercise 4.3
Print the sum of the squares of 1, 2, 3 and 4.
A correct answer prints:
30 values: ( )
One answer
# 0 [ # 1 # 2 # 3 # 4 ] { dup * + } foreach debug
30 values: ( )
Exercise 4.4
Define a word fib that gives the nth Fibonacci number (fib 0 is
0, fib 1 is 1, and each one after is the sum of the two before it), and print
fib 15.
A correct answer prints:
610 values: ( )
One answer
{ dup # 2 swap less-than
{ }
{ dup -- fib swap -- -- fib + }
either } => fib
# 15 fib debug
610 values: ( )
Exercise 4.5
From the list [ # 3 # 8 # 1 # 9 # 4 ], make the list of the
elements greater than 3, keeping their order.
A correct answer leaves:
values: ( ( 8 9 4 ) )
One answer
[ [ # 3 # 8 # 1 # 9 # 4 ] { dup # 3 less-than { } { del } either } foreach ]
values: ( ( 8 9 4 ) )