The ABC Programmer's Handbook by Leo Geurts, Lambert Meertens, and Steven Pemberton
[An Example] [Types [ Numbers] [ Texts] [ Compounds] [ Lists] [ Tables] [ Generic operations]] [User-defined Commands and Functions] [Names and Locations] [Control Commands] [Random] [Tests] [Input/Output] [Refinements]
HOW TO PRINT CELSIUS FROM a TO b: PUT a, b IN lo, hi IF lo > hi: PUT hi, lo IN lo, hi \Swap hi and lo FOR f IN {lo..hi}: PUT (f-32)*5/9 IN c WRITE f, "Fahrenheit =", 2 round c, "Celsius" /
Once this piece of ABC (the definition of
a command PRINT CELSIUS
for listing
temperatures in degrees Fahrenheit converted to degrees Celsius)
has been entered, you can give this command:
PRINT CELSIUS FROM 40 TO 45
You then get the following on your screen (we use a slanting font for output from ABC):
40 Fahrenheit = 4.44 Celsius 41 Fahrenheit = 5.00 Celsius 42 Fahrenheit = 5.56 Celsius 43 Fahrenheit = 6.11 Celsius 44 Fahrenheit = 6.67 Celsius 45 Fahrenheit = 7.22 Celsius
The text of this how-to is straightforward:
a
and b
, which are in lower
case. PUT
has an expression to the left of
IN
, and an address to the right. The expression is
evaluated to give a value, and the address to give a
location. The value is then put in the location. As we shall
see later, this multiple PUT
command in fact puts one
compound value in one compound location. The second
PUT
command swaps the values of lo
and
hi
. \
', and
continues to the end of the line. A comment can come after a command,
or occur on a line by itself.IF
or FOR
, controls
the indented commands following the corresponding colon. Thus, the
user need not bother about pairs of BEGIN
and END
or
other delimiters for grouping commands. For the same reason, the whole
body of the how-to has been indented. If there is only one controlled
command, the whole construction may be displayed on a single
line:
IF a < 0: PUT -a IN a
FOR
command traverses the list of numbers which
starts at lo
and ends with hi
.round
function in the WRITE
command rounds its right operand to 2 digits after the decimal
point, as indicated by its left operand./
at the end of the WRITE
command specifies transition to a new line.In
the ABC system a friendly editor takes care of the tedious aspects
of typing the program, such as indentation and other layout
matters, capitals, keywords such as PUT
and
WRITE
, etc. This is explained in chapter 3,
Using ABC.
The power of ABC is largely due to its carefully designed system of data types and associated operations. There are two basic types - numbers and texts and three structures creating new types from existing ones - compounds lists and tables
Integers, that is, whole numbers, in ABC are unusual in that there
is no restriction on length. Furthermore, integers are just a special
case of exact i.e. rational, numbers (integral
fractions). For example, 1.25
is an exact
number. Calculations on exact numbers with the operators +
,
-
, *
(multiplication) and /
(division),
give exact results.
In addition to exact numbers, there are approximate
(floating point) numbers. The operator ~
is used for
conversion to an approximate number: ~22/7
does not return an
exact, but an approximate number. Calculations with the mathematical
functions like root
, sin
, and log
, return
approximate numbers. The function exactly
converts an
approximate number to exact.
Mixed arithmetic is allowed, as in 2*sin(x-1)
.
Operations: | Examples: |
---|---|
plain arithmetic | +x , x+y , -x , x-y , x*y , x/y |
to the power | 3**2 = 9 |
round upwards | ceiling 3.68 = 4 |
round downwards | floor 3.68 = 3 |
round to nearest integer | round 3.68 = 4 |
round to n digits after decimal point |
1 round 3.68 = 3.7 |
sign | sign 3.68 = 1 |
absolute value | abs(-3.68) = 3.68 |
square root | root 2 = ~1.4142135623731 |
n -th root |
3 root 2 = ~1.2599210498949 |
remainder after division | 27 mod 10 = 7 |
numerator of exact number | */ 1.25 = 5 |
denominator of exact number | /* 1.25 = 4 |
natural logarithm | log 10 = ~2.302585092994 |
logarithm to base b |
2 log 8 = ~3 |
exponential function | exp 1 = ~2.718281828459 |
trigonometric functions in radians | sin x, cos x, tan x, arctan x, angle(x,y) |
trigonometric functions in degrees | 360 sin x, 360 cos x, 360 tan x, 360 arctan x, 360
angle (x, y) |
distance from origin | radius(x, y) |
The functions */
and /*
yield the numerator and
the denominator of an exact number. For this purpose, fractions are
automatically reduced to lowest terms; so 1.25 = 125/100
is
reduced to 5/4
. Again, there is no restriction on the lengths
of the numerator and denominator. The following definition for the
greatest-common-divisor function of two exact positive numbers uses
this property (as long as a
and b
are not 0):
HOW TO RETURN gcd(a, b): RETURN a / */(a/b)
"Merry Christmas"
is an example of a text. Like numbers, there
is no restriction on the length of texts.
Operations: | Examples: |
---|---|
join | "now"^"here" = "nowhere" |
repeat | "-"^^5 = "-----" |
tail | "nowhere"@3 = "where" |
head | "nowhere"|2 = "no" |
number of characters | #"nowhere" = 7 |
number of occurrences | "e"#"nowhere" = 2 |
selection | "nowhere" item 4 = "h" |
traverse the characters | FOR c IN "nowhere": |
smallest character | min "nowhere" = "e" |
largest character | max "nowhere" = "w" |
convert to upper case | upper "NowHere" = "NOWHERE" |
convert to lower case | lower "NowHere" = "nowhere" |
strip spaces | stripped " nowhere " = "nowhere" |
For
the operations @
, |
,
and item
, the elements of a text are
numbered from 1 to the length of the text.
The
operations @
and |
may
be combined to yield any subtext; for example, to obtain the subtext
starting at position 3 and having length 4, we may write:
"nowhere"@3|4 = "wher"
Such text selections may also be used as addresses. For instance, after
PUT "nowhere" IN word PUT "bless" IN word@3|4
the
location word
contains "noblesse"
(since wher
has been replaced by bless
).
A compound is a sequence of values of possibly different types. It
is like a record in other languages, but it has no field
names. Compounds are useful for multiple PUT
's, for
parameters, for multi-dimensional tables (t[i, j]
), but also
as values to be put in a table (instead of separate values put in
separate tables under the same key). Examples:
PUT a, b, c IN b, c, a \cyclic permutation PUT "May", 22 IN anniversary PUT 1813, anniversary IN date PUT date IN year, (month, day) PUT now IN (year, month, day, hour, minute, second)
Operation: | Example: | Gives: |
---|---|---|
Date and time | WRITE now |
(1995, 7, 26, 13, 13, 16.976513) |
The
function now
returns the date and time now,
with the seconds accurate to that supplied by the operating
system.
A list is a sorted sequence of values with multiple occurrences allowed. For example,
WRITE {"u"; "e"; "a"; "y"; "o"; "i"}
gives {"a"; "e"; "i"; "o"; "u"; "y"},
WRITE {3; 2; 1}
gives {1; 2; 3}, and
FOR i IN {"ay"; "i"; "eye"; "aye"}: WRITE #i
gives 2 3 3 1 (which is because {"ay";
"i"; "eye"; "aye"}
gives {"ay"; "aye"; "eye"; "i"}
).
The
items of a list may
be of any type, but all must be of the same type (all texts, or all
numbers, etc.) Since there may be multiple occurrences of an entry,
you may have lists like {1; 1; 1}
.
Sorted lists of any type work because an order is associated with each type. For instance:
2.99 < 3.00 "" < "Z" < "a" < "az" < "b" ("b", "c") < ("z", "a") {} < {"a"; "z"} < {"b"}
Instead of {1; 2; 3; 4; 5; 6; 7}
the range
notation {1..7}
may be used; similarly, {"a".."c"}
stands for {"a"; "b";
"c"}
. Several of these ranges may be combined: the list {"A".."C"; "a".."c"}
contains six items.
Operations: | Examples: |
---|---|
initialise | PUT {"wart"; "eye"; "mouth"} IN face |
add item to list | INSERT "nose" IN face |
remove one instance | REMOVE "wart" FROM face |
number of items | #face = 4 |
number of occurrences | "eye"#face = 2 |
traverse the items | FOR f IN face: INSERT f IN f2 |
smallest (= first) item | min {3; 2} = 2 |
largest (= last) item | max {3; 2} = 3 |
selection | {1; 9; 8; 9} item 2 = 8 |
Because lists are kept sorted there is no need for sorting programs. If a different order is required for the items of a list, it is often useful to create a list of compounds. Suppose texts are wanted in length order:
PUT {} IN new.face FOR feature IN face: INSERT #feature, feature IN new.face
To output the texts in the desired order, you can then use:
FOR length, feature IN new.face: WRITE feature /
(note the use of a multiple name following FOR
) which
would produce:
eye eye nose mouth
Tables are like arrays or tables in other languages, but the keys (= indices) may be of any type, and need not be consecutive:
PUT {} IN price PUT 3.50 IN price["jam"] PUT 1.95 IN price["bread"] PUT 2.45 IN price["butter"] WRITE price
gives as output on the screen:
{["bread"]: 1.95; ["butter"]: 2.45; ["jam"]: 3.50}
Notice that the entries are sorted on the keys. All keys of a table must have the same type, but just as with lists, may be of any type. The items (the stored values) must also all have the same type, but, of course, the types of the keys and the items need not be the same.
Operations: | Examples: |
---|---|
initialise | PUT {} IN count |
add entries | FOR f IN face: PUT 0 IN count[f] |
modify entries | FOR f IN face: |
the list of keys | keys count = {"eye"; "mouth"; "nose"} |
length | #count = 3 |
number of occurrences in the items | 2#count = 1 |
number of occurrences in the keys | "nose" # keys count = 1 |
traverse the items | FOR i IN count: PUT s+i IN s |
traverse the keys | FOR f IN keys count: WRITE f, count[f] /
|
smallest item | min count = 1 |
smallest key | min keys count = "eye" |
largest item | max count = 2 |
largest key | max keys count = "nose" |
selection of an item | count item 2 = 1 |
selection of a key | (keys count) item 2 = "mouth" |
delete entry | DELETE count["nose"] |
The
keys
function plays an important role in
nearly all applications of tables.
There
is one function on texts, split
, that splits
a text into its space-separated words and returns these as a
table:
split " now here " = {[1]: "now"; [2]: "here"}
As you may have noticed, several of the operations mentioned work equally for texts, lists and tables. Texts, lists and tables together are called trains. The generic operations are:
Operations: | Meaning: |
---|---|
# |
the length |
min |
the smallest item |
max |
the largest item |
item |
selection of an item |
The
FOR
command also works generically on
trains.
When a generic operation is applied to a table, it is the items of
the table that are handled, and not the keys, so min t
, where
t
is a table, gives the smallest item, and min
keys t
gives the smallest key.
For
min
and max
there are
also versions with two operands: i min t
gives the smallest item of t
that is larger
than i
. Similarly, i max
t
gives the largest item of t
smaller
than i
. For instance,
"i" min {"a"; "e"; "i"; "o"; "u"; "y"} = "o" "i" max {"a"; "e"; "i"; "o"; "u"; "y"} = "e"
As
shown in the PRINT CELSIUS
example, new
commands may be defined with how-to's:
HOW TO APPEND tail TO text: PUT text^tail IN text
HOW TO PREPEND head TO text: PUT head^text IN text
HOW TO PRINT text VERTICALLY: FOR c IN text: WRITE c /
HOW TO REVERSE text: PUT "" IN reverse FOR c IN text: PREPEND c TO reverse PUT reverse IN text
Once these have been given, the commands
PUT "here" IN word PREPEND "now" TO word REVERSE word
make word
equal to "erehwon"
. If we now say
REVERSE word@4|2
word
is made equal to "erewhon"
.
So a command how-to is to ABC what a procedure or sub-routine is to
other languages. The general appearance of user-defined commands is
the same as that of predefined commands such as PUT
... IN
... (though no control commands of the type
WHILE
... : can be defined by the user).
Similarly, users may define their own functions, such as this one which returns the set of different items occurring in a given list:
HOW TO RETURN elements collection: PUT {} IN unique FOR element IN collection: IF element not.in unique: INSERT element IN unique RETURN unique
The name of the function (elements
) and its template
operand (collection
) occur in the first line (values passed
to functions are called operands and those passed to commands
are called parameters. The how-to should contain one or more
RETURN
commands, specifying the result, which may be of any
type, just as the operand may be.
As
well as functions with one operand, you may also define functions with two
operands, and
functions without operands. Here is a function called with
that has two operands list1
and list2
, and a
function origin
that has no operands:
HOW TO RETURN list1 with list2: FOR i IN list1: INSERT i IN list2 RETURN list2
HOW TO RETURN origin: RETURN (0, 0, 0)
Functions with more than two operands have to be written as functions with one or two compound operands, e.g.,
HOW TO RETURN (x, y, z) rotated (pitch, roll, yaw):
The
function with
above appears to change the
value of its operand, but such a change does not affect any
location outside the how-to: it changes a copy of the operand which is thrown away upon
termination of the invocation of the function.
In
contrast to functions, the execution of a command defined by a HOW
TO
does change the environment when something is PUT
IN
a parameter; in
fact, that is the very purpose of commands. When a operand is given to a
function, only its value is passed, whereas in the case of a
command, the value of the parameter is passed if it has one, and
then after the execution of the command, if the value has been
changed it is passed back.
The
operands for functions and the parameters for commands require no
parentheses (except
to indicate priorities). For instance, you can write sin x
and 2*sin x
.
However, you cannot write sin x * 2
since it
is ambiguous, and the system will tell you so. You must write
either (sin x) * 2
or sin(x*2)
, depending on which you mean. Values of any type may be passed to a
how-to, as long as they match up with the operations used in the
how-to. For instance, the elements
function
above will work on any train as operand:
WRITE elements "Mississippi"
gives as output
{"M"; "i"; "p"; "s"}
If we use a command such as
PUT {2; 3; 5; 7} IN primes
as an
immediate command
(as opposed to inside a how-to), a location is created - unless one already
exists - whose name is primes
. The location
stays in existence until the command DELETE
primes
is given, and so is referred to as a permanent location. Names of locations used
inside a how-to are private to that how-to, and the corresponding
locations are temporary: they are alive only as long as the
invocation of the how-to lasts.
One
way of importing permanent locations into a how-to is by using
parameters, but there is also another way. If, in the example of
the previous section, there is only a single text for APPEND
, REVERSE
, etc., to
work on, it is a nuisance to have to pass it as a parameter each
time. The how-to's may then be changed to:
HOW TO APPEND tail: SHARE text PUT text^tail IN text
HOW TO PRINT VERTICALLY: SHARE text FOR c IN text: WRITE c /
etc.
SHARE text
indicates that the name text
in this how-to will not use a location
private to the
how-to, but will refer to a shared permanent location, either
already in existence, or to be created during the invocation of the
how-to. Other how-to's wishing to refer to the same text
should now also SHARE
text
, since otherwise their use of the name text
would be private and would introduce a
temporary location.
A
location should be used consistently for values of one type.
Private names may be used for different types of values during the
execution of different invocations, but during one invocation the
corresponding location may contain only values of the same type. So, if we have
defined this SWAP
command:
HOW TO SWAP a AND b: PUT a, b IN b, a
we may use it at one time to swap two numbers, at another time to swap two texts, but never to swap a number and a text.
The system checks this partly statically, i.e. at the time the how-to's are typed in, partly dynamically, i.e. during execution time. Checking is done on a basis of consistency, not on a basis of conformity to declarations, which do not exist in ABC.
There are two conditional commands. In addition to
IF test:
(no ELSE
allowed), ABC also has
SELECT: test1: ... test2: ... test3: ...
The
first test to succeed determines which alternative is to be executed. At least
one test must succeed. Instead of the last test, the keyword ELSE
may be used, which always succeeds:
SELECT: e > 0: INSERT e IN pos e < 0: INSERT e IN neg ELSE: PUT nzero+1 IN nzero
For
repetition, apart from FOR
... IN
..., there is also
WHILE test: ...
to repeatedly execute a group of commands while the test is true.
For leaving a command how-to before its end, the command
QUIT
is used. It may occur anywhere inside a command how-to.
The
function random
returns an arbitrary approximate number,
such that
0 <= random < 1 .
The function choice
returns an arbitrary item from a
train. For instance, the command
PUT choice {"A".."Z"} IN cap
puts
an arbitrary capital letter in cap
.
Likewise,
PUT choice "Mississippi" IN c
puts an arbitrary letter from the text "Mississippi"
in
c
. In the same way, it is possible to make an arbitrary
choice
from the items of a table. The meaning of
choice
may be described in terms of random
in the
following way:
HOW TO RETURN choice train: RETURN train item (1+floor(random*#train))
(This is another example of a how-to which may be used with operands of different types - text, list or table in this case - as long as they are consistent with the operations inside.)
The behaviour of choice
and random
is based on a
pseudo-random sequence. To make programs replicable, the sequence may
be started at a specified point by using a SET RANDOM
command, e.g. like this:
SET RANDOM "Today is my birthday."
or with an invocation with any other expression of any type as a parameter.
An order is associated with each type, so <
,
<=
, =
, >=
, >
and
<>
(unequal) are defined for all values having the same
type (but not for values of different types). Multiple comparisons
are allowed:
WHILE x < y < z: PUT f y IN y
To
discover if a list or table (or text) contains a certain item (or
character), the in
test may be used:
SELECT: i in keys count: PUT count[i]+1 IN count[i] ELSE: PUT 1 IN count[i]
Similarly, the test not.in
tests the absence of
an item.
To
discover if a number is exact or approximate, the test exact
is used:
SELECT: exact x: WRITE */ x, "over", /* x ELSE: WRITE "~", x
Tests
may be combined with AND
, OR
and NOT
:
IF i in keys t AND t[i] > 2: ... IF NOT (c = "a" OR c = "b"):.
Such
combined tests are performed from left to right. In an AND
combination, evaluation stops as soon as a
failing test is encountered; in an OR
combination it stops at the first succeeding test.
ABC
also has versions of the mathematical quantifiers SOME
, EACH
and NO
:
IF SOME d IN {2..n-1} HAS n mod d = 0: WRITE n, "has factor", d
If
the SOME
test succeeds, the first value found
to satisfy the test following HAS
is put in
the location of the name between SOME
and IN
. Examples of NO
and EACH
are:
IF NO year, anniversary IN birthday HAS year = 1813: WRITE "list is incomplete"
SELECT: EACH name IN keys birthday HAS name|1 in "ABC": WRITE "ABC-composers abound" ELSE: WRITE name
Just
as a new function may be defined with HOW TO
RETURN
, a new predicate to be used in tests may be defined with
HOW TO REPORT
:
HOW TO REPORT a includes b: REPORT EACH x IN b HAS x in a
In a predicate how-to, REPORT
is used instead of
RETURN
to indicate the outcome. To avoid REPORT 1 =
1
or REPORT 0 = 1
, the commands SUCCEED
and
FAIL
are available as well. Another version of the
includes
how-to might be:
HOW TO REPORT a includes b: FOR x IN b: IF x not.in a: FAIL SUCCEED
As
with HOW TO RETURN
, operands are passed by value and the
environment is not affected by changes made by a predicate
how-to.
Input from the user at
the keyboard is read by a READ
command, such as
READ size EG (0, 0)
where the expression following EG
specifies the type of
the expression to be entered (here a compound of two numbers). The
type of the expected expression may be given by, for example,
0
or ""
, or by any other expression. In the
following example, the READ
command demands the input to be
of the same type as the items of the list legal.moves
:
READ move EG min legal.moves
READ
prompts the user, who may then enter any
expression of the desired type, using shared names and built-in or
user-defined functions.
READ
with an EG format requires input texts to be
quoted, just as in ABC itself. For situations where this is a
nuisance, another version of the READ
command
is available:
READ line RAW
which
interprets the input line as a text and stores it in the location
line
.
For
output to the
screen, a WRITE
command is used. As shown above, WRITE
can handle expressions of any type. Apart
from the operations mentioned in Texts, the following operations are useful
for formatting purposes:
Operation: | Examples: |
---|---|
align left | 7*8<<9 = "56 " |
align right | 7*8>>9 = " 56" |
centre | 7*8><9 = " 56 " |
insert value of expression | "1K is `2**10`" = "1K is 1024" |
These
operations work for values of any type, and aren't confined to WRITE
commands; for instance:
PUT "1K is `2**10`" IN message
Hierarchical programming in ABC is aided by refinements. These are like light-weight procedures, but:
Refinements come in three kinds, analogous to the three kinds of how-to's. All three kinds are seen in this how-to to compute the average of a sequence of positive numbers. Note that just as with commands, functions and predicates, the name of a command-refinement is in capital letters, and the names of function- and predicate-refinements are in lower-case:
HOW TO AVERAGE: INITIALISE GET INPUT WHILE valid: TREAT GET INPUT OUTPUT INITIALISE: PUT 0, 0 IN sum, n GET INPUT: WRITE "Number: " READ i EG 0 TREAT: PUT sum+i, n+1 IN sum, n valid: REPORT i > 0 OUTPUT: WRITE "The average is:", average average: RETURN sum/n
Note
that function refinements contain a RETURN
command, and predicate refinements a REPORT
command. Similarly, command refinements may be left early with the
QUIT
command. For instance, here is a way of
writing the elements of a train until an element is greater than
100:
LOOP: FOR x IN train: IF x > 100: QUIT WRITE x
Of course, refinements become more useful as programs become longer, and you will see many in the coming chapters.
The ABC Programmer's Handbook by Leo Geurts, Lambert Meertens, and Steven Pemberton
Copyright © 1990, 2002, Leo Geurts, Lambert Meertens, and Steven Pemberton. All rights reserved.