The ABC Programmer's Handbook by Leo Geurts, Lambert Meertens, and Steven Pemberton

CHAPTER 1

A Quick Look at ABC

[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]

An Example

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:

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.

Types

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

Numbers

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)

Texts

"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":
    PUT freq[c]+1 IN freq[c]
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).

Compounds

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.

Lists

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
INSERT "eye" 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

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:
    PUT count[f]+1 IN count[f]
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"}

Generic operations

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"

User-defined Commands and Functions

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"}

Names and Locations

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.

Control Commands

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.

Random

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.

Tests

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/Output

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

Refinements

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