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.

programasks
x # 10 less-thanis 10 less than x?
# 10 x less-thanis x less than 10?
x # 10 greater-thanis 10 greater than x?
x # 10 equalsis 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 ) )