URL http://www.cs.hmc.edu/~keller/rex

Convenience link to CS 60 and to the rex liteReference Card.

Last updated 5 September 1999 by keller@cs.hmc.edu.Please report any errors or inaccuracies to keller@cs.hmc.edu.

rex Reference Card

This card also forms the basis of the interactive help file callable from rex, which might explain its slightly unusual organization. The rex help system looks for the keyword "bullets"which mark each item.

Help

One way to get help interactively is to type

*h (return) 

to the rex read loop. This puts you into the help loop, wherein you can type terms, or parts of terms and get descriptions from this document.

To return to the rex read loop from the help loop, type the character control-d.

Another way to invoke help on a topic is to call, within rex,

help(Topic);

It is not necessary to put quotes around the topic. Help is available on the following topics, as well on all individual functions.

Read Loop

rex runs an interactive "read loop", also called read-eval-printloop. This means that the following cycle repeats until the user types end-of-file (e.g. control-d):

In some cases, it is a little more complicated than this: the evaluation can be interleaved with printing the answer. This ishelpful when the answer is very long, and essential when the answeris infinite. An evaluation can be interrupted (seeinterrupting rex).

Quick Commands

Quick commands provide an alternative for entering system and helpcommands with a minimum of key strokes. A quick command is identifiedby a single line starting with * and terminated by return (no ;) Thefollowing quick commands are available:

*i Filename
Load the named file (no quotes around Filename).
*h
Enter the help loop.
*q
Toggle "quotes" mode. Only output is affected. In this mode, double quotes are shown as strings and single quotes are shown around characters.
*r
Toggle "readline" mode. In this mode, you can use emacs-like commands to edit single lines and move backward and forward in the history list of commands. (Note: Currently in readline mode, suspending with control-Z is ineffective and causes problems.)
*t
toggle the function trace
*N
where N is a positive number: redo expression number N.
*-N
where N is a positive number: redo the expression N expressions ago
**
redo the previous expression (same as *-1)
 
Comments

Comments are as in C++. They either start with

// and continue to end-of-line, or start with

/* and continue to */.

Currently the latter comments cannot be recursive (i.e. you can'thave /* .... */ within /* ....*/).

Dynamic Types and Storage

rex is a dynamically-typed language, meaning that types are associated with values, rather than with variables. A given variablecan be bound to any type of value and there are no type declarations. Many of the functions are polymorphic (accepting more than one type of argument).

The down-side of this is that types have to be checked for sensibility at run-time. For example, it doesn't make sense to multiply one string by another. Thus there may be errors about whichyou don't find out until run-time, but which would have been caughtat compile-time by a statically-typed language. rex handles such errors for you by returning an error value when types don't makesense. A list of types is given with the description of the functiontype which will indicate the type of adata item.

Another sense in which rex is dynamic is that there are no storage allocation commands. Data items, typically lists, get created as theresult of function evaluations. But the programmer does not preallocate space for these results. Reclamation of unused storage issimilarly handled by the system.

Expressions

There are three types of rex expressions:

All expressions to be evaluated are terminated with asemi-colon. (end-of-line doesn't terminate an expression,because expressions can take an arbitrary number of lines, and it isnot generally evident when there isn't more to an expression.)

Definitions

Definitions bind variables and function names. A definitionappears as

LHS = RHS;

where LHS is the variable or variables being defined, and RHS iswhat defines them. Examples are:

cube(x) = x*x;         // defines the cube function  alist = [1, 2, 3, 4];   // defines a list variable called alist  [x, y, z] = foo(alist); // defines variables x, y, and z,

Caution: While definitions have a certain similarity toassignments (e.g. in C), they should not be used that way. Theability to reassign is simply for convenience in interacting with thesystem. At the core, rex is a functional language and is designed tohelp learn to program without assignment statements. Global definitions have a value 1 if the definition succeeds and 0 if not.The reason a definition might not succeed is that the RHS valuesdon't match the LHS pattern. For example, in

[x, y, z] = [1, 2]; 

the list on the right has two values whereas the one on the lefthas three. On the other hand

[x, y | z] = [1, 2];

will succeed and bind x to 1, y to 2, andz to [ ]. See also localdefinitions.

Rules

Rules define parts of functions by using pattern matching andguards, and are collected together by rex to make a definition. A rule has the form: LHS => RHS; (The symbol=> an = followed by a > and is read "yields"). The following are examples of rules (for the greatest-common-divisorfunction):

gcd(0, y) => y;

gcd(x, y) => x <= y ? gcd(y % x, x);

gcd(x, y) => gcd(y, x);

The second rule above uses a guard, anexpression which must be true in order for the rule to apply. Asanother example, the following rules use pattern matching to find thelast element in a non-empty list.

last([X]) => X;  last([_ | X]) => last(X);
Evaluable Expressions

Although all expressions have a value, the remaining kinds ofexpressions are entered for purposes of evaluation. They yield avalue which is printed. They can also have side-effects, but the routine use of side-effects is discouraged. The computations of theseexpressions are done in the context of definitions and rules.

For example, to use the gcd and lastfunctions defined above in a computation, we'd need to enter anexpression:

gcd(56, 72); 

rex would respond with the answer 8. If we entered:

last([1, 2, 3, 4, 5]);

rex would respond with the answer 5.

Variables

A variable is a sequence of contiguous characters starting with aletter (upper or lower case) or underscore, and containing letters,underscores, and digits. Names of functions are considered to bevariables bound to anonymous function values.

Operators

The following kinds of operators are available:

Arithmetic, logical, and relational operators are pretty much thesame as in C and C++.

Precedence and Grouping

The following lists operator precedence tightest to weakest. Alloperators group left unless otherwise indicated.

Arithmetic Functions

These functions are defined as follows:

+ as in N1 + N2 + .... + Nn
with 2 or more numeric arguments, + adds the arguments together.
with 2 list arguments, adds the arguments together component-wise.
with 1 argument, +(N) is defined as the function (X) => X+N.
 
- as in N1 - N2
with 2 arguments, - subtracts its second argument from its first.
with 2 list arguments, subtracts the arguments component-wise.
As a prefix operator -(....) is unary minus, i.e. negates its argument.
* as in N1 * N2 * .... * Nn
with 2 or more numerical arguments, * multiplies the arguments together.
with 2 list arguments, multiplies the arguments component-wise.
with the first argument a scalar and the second a list, multiplies each element in the list by the scalar.
with 1 argument, *(N) is defined as the function (X) => X*N.
/ as in N1 / N2
with 2 arguments, / divides its first argument by its second.
with 2 list arguments, divides the elements of the first list by those in the second.
with 1 argument, /(N) is defined as the function (X) => X/N. The result is integer if all arguments are integer, otherwise it is floating.
% as in N1 % N2
gives the remainder of dividing the first argument by the second. Only integer arguments are supported.
abs
abs(N) returns the absolute value of N.
ack
Ackermann's function ack(N, X, Y) returns the value of X op Y at the Nth level of the hierarchy of ops +, *, pow, ...
ceil
ceil(N) returns the smallest integer >= N;
divides
divides(N1, N2) returns 1 if integer N1 divides N2 with no remainder.
divides(N2) is the function which, with argument N1 will give the value divides(N1, N2).

For example, divides(3)(N1) will return 1 whenver N1 divides 3.

exp
exp(N) returns the exponential function of N, i.e. pow(e, N) where e is base of the natural logarithm.
fac
fac(N) returns the factorial of N (N*(N-1)*(N-2)*....*2*1).
finite
finite(Expression) returns 1 or 0 depending on whether the argument is neither of Infinity nor -Infinity in IEEE floating point.
floor
floor(N) returns the largest integer <= N;
float
float(N) returns a floating point value of a number N, whether N is floating or integer
frexp
frexp(N) returns a list of a floating point number and an integer. Every non-zero number can be written uniquely as x * 2^n where the ``mantissa'' (fraction) x is in the range 0.5 < |x| < 1.0, and the ``exponent'' n is an integer. frexp returns the mantissa and exponent in the form of a list of two elements. When the value is zero, both elements are 0.
ldexp
ldexp(L) constructs a floating-point number from a list consisting of a floating-point mantissa and an exponent. In this form, it is the inverse of frexp
ldexp(M, E) takes the mantissa and exponent explicitly rather than in a list.
integer
integer(N) returns the integer part of N as an integer, whether N is integer or floating. If N is a character, it returns the character code (e.g. ascii) for N.
isnan
isnan(Expression) returns 1 or 0 depending on whether the argument is a NaN ("not a number" in IEEE floating point). Note that by IEEE rules, two Nan's are always unequal and a given Nan is not even equal to itself. We currently violate the latter rule: x == x will always be true.
is_prime
is_prime(N) returns 1 if N is prime, 0 otherwise. Only applies to integers.
make_number
make_number(A) If A is a string, make_number tries to interpret it as a decimal numeral (integer or floating, in that order) and create a number from it. Similarly, if A is a char for a decimal digit. If A is a number, returns that number. Otherwise returns an error.
minus
minus(N) returns the negative of N, i.e. the same as -N.
pow
pow(N1, N2) returns the N1 raised to the power N2. The result is floating point if either argument is, otherwise it is integer.
sq
sq(N) returns the square of N.
sqrt
sqrt(N) returns the square root of N. If N is an integer, the result is the largest integer M such that M*M <= N.
 
Relational Functions

These apply to more than just numeric values. For example, listsand arrays can be compared in lexicographic order.

==
A1 == A2 evaluates to 1 if the arguments are equal, otherwise 0.
==(A2) is the function which, with argument A1, gives the value of A1 == A2.
!=
A1 != A2 evaluates to 1 if the arguments are unequal, otherwise 0.
!=(A2) is the function which, with argument A1, gives the value of A1 != A2.
>
A1 > A2 evaluates to 1 if A1 is greater than A2.
>(A2) is the function which, with argument A1, gives the value of A1 > A2.
<
A1 < A2 evaluates to 1 if A1 is less than A2.
<(A2) is the function which, with argument A, gives the value of A1 < A2.
>=
A1 >= A2 evaluates to 1 if A1 is greater than or equal A2.
>=(A2) is the function which, with argument A1, gives the value of A1 >= A2.
<=
A1 <= A2 evaluates to 1 if A1 is less than or equal A2.
<=(A2) is the function which, with argument A1, gives the value of A1 <= A2.
max
max(A1, A2, ...., An) evaluates to the largest of the arguments. There must be at least one argument.
min
min(A1, A2, ...., An) evaluates to the smallest of the arguments. There must be at least one argument.
 
Booleans

The Boolean model used by rex is: 0, [ ] (the empty list), andfailure values are false, and anything elseis true. When a built-in returns a Boolean, it is either 0 or 1.

Logical Operators

The logical operators are:

!
!A1 returns 1 if the argument is false, otherwise returns 0.
&&
A1 && A2 && .... && An evaluates arguments in sequence, until one is false, The first false argument is returned. Otherwise 1 is returned.
||
A1 || A2 || .... || An evaluates arguments in sequence, until one is not false. The first non-false argument is returned. Otherwise 0 is returned.
implies
implies(A1, A2)
A1 is evaluated. If it is not false, the result is A2. Otherwise the result is 1.
 
Lists and List Functions

List formation is represented by [X0, X1, ...., Xn-1]. A listresult will show in this fashion as well, unless it is converted tosome other format. The null list is [ ].

Lists may be written to and read from files as S expressions bysingle commands.

A list may be formed from an item and an existing list by [X | L],meaning to construct the list consisting of X followed by the elements of L. Another way to get the same effect is cons(X, L). Withrespect to the list thus created, X is the "first" of the list and Lis the "rest".

This notation may be generalized: [X0, X1, ...., Xn-1 | L] formsthe list starting with the n elements indicated, followed by the elements of L.

An "improper list" is formed by [X | Y] where Y is not a list. Aresult of this form will show as [X | Y]. Programming will generallybe simplified if improper lists are avoided.

 

Incrementaland "infinite" lists may be created by deferring the rest of thelist: In [X |$ L] the $ acts to defer the evaluation of L. Taking therest of the list will give the seed $L which is not further evaluateduntil needed (e.g. in the case of checking to see whether the rest isthe empty list.

Some list functions are:

Other list functions may be found undersequence functions.

list
[A1, ...., An] or list(A1, ...., An) makes a list of the values of any number of arguments. Note carefully how list differs from cons.
cons
[A1, .... | An] or cons(A1, ...., An) Function cons (of an arbitrary number of arguments, but often 2) makes a list of the values of its arguments, the value of the last argument becoming the tail of the new list. There must be at least one argument.
first
first(A) returns the first element of a non-null list.
rest
rest(A) returns a list of all but the first element of a non-null list.
null
null(A) returns 1 if its argument is the empty list and 0 otherwise.
second
second(A) returns the second element of a list with at least two elements.
third
third(A) returns the third element of a list with at least three elements.

Other selections from lists may be made by treating the list as a function from {0, 1, 2, ..., length-1} to values: i.e. L(i) is the ith element of L.

range
range(N1, N2):

If N1 <= N2, range(N1, N2) yields the list [N1, N1+1, N1+2, ...., N2].

If N1 > N2, range(N1, N2) yields the list [N1, N1-1, N1-2, ...., N2].

range(N1, N2, K) yields the list [N1, N1+K, N1+2*K, ...., N2] in case that N1 <= N2 and K is positive, or N1 >= N2 and K is negative. Otherwise the result is [ ].

Examples:

range(1, 9) ==> [1, 2, 3, 4, 5, 6, 7, 8, 9] range(9, 1) ==> [9, 8, 7, 6, 5, 4, 3, 2, 1]  range(1, 9, 2) ==> [1, 3, 5, 7, 9]  range(9, 1, -2) ==> [9, 7, 5, 3, 1]
primes_to
 
primes_to(N) yields the list of primes from 2 up to the highest prime less than or equal to N.

 

Incremental and Infinite-List Functions
from
from(N) creates the infinite list [N, N+1, N+2, ...]. If N is floating, the items will be floating. Otherwise all items will be fixed.

from(N, K) creates the infinite list [N, N+K, N+2*K, ...]. If N is floating, the items will be floating. If K is floating, but N is not, all but the first item will be floating. Otherwise all items will be fixed.

primes
primes() creates the (infinite) list of primes starting with 2.
primes_from
primes_from(N) creates the (infinite) list of primes starting with the first prime not less than N.
random
random() creates an (infinite) list of pseudo-random non-negative integers. random(M, N) creates an (infinite) list of pseudo-random integers between M and N inclusive.

 

Strings and String Functions

A literal string must be in double quotes, e.g.

"this is a string" 

Strings are distinguished from single characters, which are insingle quotes, e.g. 'x'. In either case, backslash(\) can be used as an escape character. The standard Cescape codes apply:

\n is newline \t is tab \\ is backslash \f is form-feed \b is backspace \a is bell \v is vertical tab \r is carriage return 

Note: As with sequences, a particular character of a stringmay be extracted by treating the string as a function: S(i),where i is between 0 and (length of the string) -1 yields theith character of S.

concat
concat(S1, ...., Sn) concatenates the strings to create a new string.
explode
explode(S) creates a list of chars from string S.
lconcat
lconcat(L) concatenates the strings in list L to make one single string.
lconcat(L, S) concatenates the strings in list L interposing the string S between successive words (but not at the very end) to make one single string.
implode
implode(L) creates a string from a list of characters L.
length
length(S) gives the length of string S.
make_string
make_string(A) makes a string from argument A, where A is an integer, floating, or single char.
words
words(S) creates a list of strings from a single string. The strings are "words" in the sense that they are non-whitespace characters separated by some amount of whitespace.
 
Character Functions
 
Arrays and Array Functions

Selections from arrays may be made by treating the array as afunction from {0, 1, 2, ..., length-1} to values: i.e. A(i) is theith element of A.

array
array(A1, ...., An) yields an array of the values of its arguments.
array_from_list
array_from_list(L) yields an array with the elements of the argument, a list, as elements.
leaves
leaves(A) treats the array as the root of a tree, and returns a list of the leaves of the tree. The tree can have arrays or lists as nodes.
leafcount
leafcount(A) is the length of leaves(A)
length
length(A) gives the number of elements in the array.
length(A, N) destructively changes the number of elements in the array to N. If N is greater than the former length, then new elements are initialized to 0. Caution: This is not a function. It has a side-effect and should be used carefully, as the array might be shared.
list_from_array
list_from_array(A) creates a list with the elements of an array as elements
make_array
make_array(N, F) makes an array the size of which is given by the first argument. The second argument is a function which, when applied to 0, 1, 2, .... gives the values of the corresponding elements of the array.

For example make_array(10, id) returns an array of values from 0 to 9.

set
set(Array, Index, Value) sets the Indexth component of Array to Value). Caution: This is not a function. It has a side-effect and should be used carefully, as the array might be shared.
 
Sequence Functions

These apply to both lists and arrays, as well as tostrings in a few cases. The ones which apply to incremental orinfinite, as well as finite, lists are annotated as "incremental".

all
all(P, L) yields 1 if all X occurring in L are such that P(X). Otherwise yields 0.
antiprefix
antiprefix(N, L) yields all but the first N items in L. If L doesn't have N items, yields []. [incremental]
append
append(L, M) returns the list consisting of elements of L followed by those of M. [incremental in second argument]
assoc
assoc(X, L) yields the first list element of an association list having X as its first item. If there is no such item, yields [ ]. For example, assoc('c', [['a', 1], ['b', 2], ['c', 3], ['d', 4]]) will return [c, 3].
count
count(P, L) returns the number of X occurring in L is such that P(X).
diff
diff(B, S) forms the array or list of successive "differences", when the binary function B is viewed as "subtraction". If S = [S0, S1, S2, ... Sn], then diff(-, S) = [S1-S0, S2-S1, ...., Sn-Sn-1]. Thus diff(B, S) has one fewer element than S. diff(B, [ ]) is [ ] by convention. In general, B could be any binary operator, e.g. /. [incremental]
drop
drop(P, S) If S is an array or list, and P is a 1-ary function, then drop(P, S) is the array or list resulting from dropping those elements for which P is true. [incremental]
every
every(N, L, K) yields a list consisting of every Nth element of L starting at the Kth (where K = 0 means the first, etc. [incremental]
extract
extract(P, L) creates a new list by finding the first item X in L for which P(X) and bringing it to the front of the list. If there is no such item, this function diverges [incremental]
find
find(P, L) yields the suffix of L beginning with the first item X for which P(X). If there is no such item, yields [ ], unless L is infinite, in which case this function diverges. [See also keep.]
find_index
find(P, L) yields index (starting with 0 as the first index) of the first item X for which P(X). If there is no such item, yields -1, unless L is infinite, in which case this function diverges.
find_indices
find(P, L) yields the list of indices of all items X for which P(X). If L is infinite but there are only finitely-many such X, this function diverges after producing the indices of those X. [See also keep.] [incremental]
keep
keep(P, S) If S is an array or list, and P is a 1-ary function, then keep(P, S) is the array or list resulting from keeping those elements for which P is true. [incremental]
leaves
leaves(A) treats the list as the root of a tree, and returns a list of the leaves of the tree. The tree can have arrays or lists as nodes.
leafcount
leafcount(A) is the length of leaves(A)
length
length(S) returns the length of the list, array, or string
map
map(F, S) If S is an array or list, constructs a new array or list the same length as S, by applying F to each element of S. map(B, S1, S2) S1 and S2 should be both arrays or both lists. The result is a new array or list constructed by applying binary function B to the elements of S1 and S2, element-by-element. [incremental]
map_tail
map_tail(F, S, Tail) is like map, except that the list ends with the list Tail
mappend
mappend(F, L) Assuming L is a list, and F is a function which will return a list when applied to a member of L, returns the result of appending the elements of map(F, L) together in order.
mappend_tail
mappend(F, L, Tail) is like mappend(F, L), except that the result list ends with the list Tail.
member
member(X, L) yields 1 if X occurs in L; otherwise yields 0.
merge
merge(L, M) assuming that L and M are in order according to the built-in order <, merges them together into a single with respect to <. [incremental]
merge
merge(P, L, M) assuming that L and M are in order according to the partial order P, represented as a binary function, merges them together into a single list in order with respect to P. [incremental]
no
no(P, L) yields 1 if no X occurring in L is such that P(X). Otherwise yields 0.
pmap
pmap(F, S) is like map(F, S) except that (if multithreading is in force) each element of the result is evaluated in parallel
prefix
prefix(N, L) yields the first N items in L. If L doesn't have N items, yields all of L. [incremental]
reduce
reduce(B, U, S) Here B is a binary operator with unit U and S is an array or list. The result is the accumulation of applying B to elements in S.

For example, reduce(+, 0, S) is the sum of elements in S, while reduce(*, 1, S) is the product. Grouping is to the left, i.e. if S were [S0, S1, S2, ... Sn], then the result is B( .... B(B(B(U, S0), S1), S2) ...., Sn). [incremental]

remove_duplicates
remove_duplicates(L) returns a list with any duplicates removed [incremental]
reverse
reverse(L) returns the list consisting of elements of L in reverse order
scale
scale(F, S) If S is an array or list, constructs a new array or list the same length as S, in which each element of the original is multiplied by F. [incremental]
scan
scan(B, S) B is a binary operator and S is an array or list. The result is the array or list (of the same length as S) of partial accumulations of B applied to the elements of S. For example, scan(+, S), where S = [S0, S1, S2, ... Sn], yields [S0, S0+S1, S0+S1+S2, ...., S0+....+Sn-1]. [incremental]
some
some(P, L) yields 1 if an X occurs in L such that P(X). Otherwise yields 0.
sort
sort(S) returns an array or list (whichever S is) containing the elements of S in sorted order. The ordering is the natural one built into rex.
suffix
suffix(N, L) yields the last N items in L. If L doesn't have N items, yields all of L. [incremental]
zip
zip(L, M) yields the list formed by alternating an element of L with an element of M [incremental]

 

Transcendental Functions
log
log(N) returns the natural logarithm of N.
log(N1, N2) returns the logarithm of N2 base N1.
sin
sin(N) returns the sine of N.
cos
cos(N) returns the cosine of N.
tan
tan(N) returns the tangent of N.
asin
asin(N) returns the inverse sine of N.
atan
atan(N) returns the inverse tangent of N.
sinh
sinh(N) returns the hyperbolic sine of N.
cosh
cosh(N) returns the hyperbolic cosine of N.
tanh
tanh(N) returns the hyperbolic tangent of N.
asinh
asinh(N) returns the inverse hyperbolic sine of N.
acosh
acosh(N) returns the inverse hyperbolic cosine of N.
atanh
atanh(N) returns the inverse hyperbolic tangent of N.
erf
erf(N) returns the "error function" of N, 2/sqrt(pi)*integral from 0 to N of exp(-t*t) dt.
erfc
erfc(N) returns the "complementary error function", 1.0 - erf(N), computed by methods that avoid cancellation for large N.

 

Input-Output Pseudo-Functions
close
close(Ostream) closes the argument Ostream. If the stream is input to a Unix process, closing is necessary for that process to terminate.
endl
endl(Ostream) sends an end-of-line to the argument Ostream.
eof
eof() indicates whether the standard input is in an end-of-file condition and clears that condition
exec
exec(Command) executes a command in the operating system (e.g. Unix). It returns a pair [Istream, Ostream]. The program sends input to the command via the Ostream and gets output from the Istream, using appropriate primitives described in this section.
inchars
inchars() yields a list of characters from the characters in the standard input. The input can be unlimited, e.g. piped in from Unix. The result is an incremental list.
inchars(Filename) yields an incremental list of characters from the characters in the named file. The list is built incrementally on demand.
insexps
insexps() yields a list of values as determined by S expressions in the standard input. The list is incremental and the input can be unlimited, e.g. piped.

 

insexps(Filename) yields an incremental list of values as determined by S expressions in the named file.
insexp
insexp(Filename) yields single value as determined by the first S expression in the named file. The file is opened, then closed.
endl
flush(Ostream) flushes the identified Ostream without sending an end-of-line.
make_istream
make_istream(Filename)
yields an istream value from a file, for use with readsexp or readchar.

 

make_istream()
yields an istream value from the standard input stream.
make_commented_istream
make_commented_istream(Filename)
yields an istream value which automatically ignores comments (from // to end-of line and from /* to */) which can then be used with readsexp or readchar.

 

make_commented_istream()
yields an istream value from the standard input which automatically ignores comments (from // to end-of line and from /* to */) which can then be used with readsexp or readchar.
make_ostream
make_ostream(Filename) yields an ostream value, e.g. for use with writesexp.
outsexps
outsexps(L) outputs an incremental list L (possibly infinite) to the standard output.
outsexps(Filename, L) outputs an incremental list L (possibly infinite) to the named file.
outsexp
outsexp(Filename, V) outputs a single value to the named file. The file is opened, then closed.
readchar
readchar() reads a single character from the standard input.
readchar(Istream) reads a single character from the istream identified.
readchars
readchars(Istream) makes stream of chars from the istream identified into an incremental list.
readline
readline() reads a single line from the standard input as a string.
readline(Istream) reads a single line, as a string, from the istream identified.
readsexp
readsexp() reads a single S expression from the standard input.
readsexp(Istream) reads a single S expression from the istream identified.
readsexps
readsexps(Istream) makes stream of S expressions from istream into an incremental list.
writesexp
writesexp(Sexp) writes a single S expression to the standard output. Note that Sexp can be a single char or a single string.
writesexp(Ostream, Sexp) writes a single S expression to the ostream identified.
 
Miscellaneous Functions
atomic
atomic(A) returns 1 if its argument is not a list or array.
builtin
builtin(Name, Arity) returns a specific builtin function given literal name and arity.

 

builtin()
returns a sorted list of built-in functions and special forms with their arities. For example:
[[!, 1],[!=, 1], [!=, 2], [%, 2], [&&, 5], [*, 1], [*, 2],[*, 5],[+, 1], [+, 2], [+, 5], [-, 1], [-, 2], [/, 1 ], [/, 2], [<, 1], [<, 2], [<=, 1], [<=, 2], [=, 3], [==, 1], [==, 2],[=>, 2], [>, 1],[>=, 1],[asin, 1], [asinh, 1], [atan, 1], [atanh, 1], [atomic, 1], [builtin, 0], [builtin, 2], [ceil, 1], [char, 1], [close, 1], [concat, 5], [cons, 2], 

etc. Each function is paired with an arity.

Note that a special meaning is attached to arity 5; rather than being a specific arity, it means that the arity is arbitrary. For example, concat(), concat(A), concat(A, B), concat(A, B,C), ... are all valid since [concat, 5] appears in the list. In some cases, such as with cons above, both arity 5 and arity 2 appear. This could indicate special over-riding treatment for the specific arity 2, although in this case it turns out to be just an efficiency hack, since cons is usually used with just 2 arguments.

deep_type
deep_type(A) returns the type of A if A is not a list, or a list of the deep_types of elements of the list if it is a list. Example: deep_type([1, [2, 3], "foo") ==> [integer, [integer, integer], string]
defined
defined() Creates a list of names (as strings) of identifiers (other than builtins) which are currently defined.
eval
eval(Expression) Expression is evaluated, then that value is treated as if it were an expression and evaluated. This allows expressions to be constructed from lists on the fly, then evaluated. To see how expressions are constructed in general, analyze them withs the quote operator. Example: eval(["+", 2, ["*", 3, 4]]) ==> 14
id
id(Datum) returns Datum (useful in certain contexts, e.g. make_array).
k
k(Datum) makes a constant function with value Datum. That is, it returns a function which, when applied to any argument, returns Datum. i.e. k(Datum)(Anything) is Datum. (useful in certain contexts, e.g. make_array).
quote
quote(Expression) The argument is not evaluated, but is converted into a literal structure of the same form used by the rex interpreter for programs. The resulting value may then be processed as a string or list, as appropriate. Example: quote(a+b*c) has the value ["+", "a", ["*", "b", "c"]]
test
test(Expression1, Expression2) evaluates the expressions and compares them for equality. Usually Expression2 is the expected answer. Prints a message indicating whether the test is "ok" or "bad".
type
type(A) returns a string naming the type of its argument A. The following types are available:
type() returns a list of strings naming the available types. Lists and arrays may have any of these types as elements, in any mixture, include lists and arrays to any number of levels of nesting.

The following functions return 1 or 0, depending on whether theargument is of the corresponding type:

See also atomic which returns 1 ifits argument is not a list or array.

sow
sow(Expression) If multithreading is available, the value of Expression is evaluated in a separate thread. Meanwhile, the expression is treated as being deferred. Any attempt to use the value will synchronize with the evaluating thread until the value becomes available.
grow
grow(Expression) Explicitly waits for the result of Expression to be evaluated, e.g. in the case that the result is that of a sow. This function is implicit in the built-in functions which require it, thus it is not often used.
spec
spec(Name, Arity) or
Name#Arity Returns a specific function given literal name and arity.

 

Side-Effect Pseudo Functions

Caution: Use of side effects is not in the spirit of a functional language. These primitives may be disabled for purposes of teaching functional programming.

set
set(Variable, Value) Changes the value of an already-defined variable, local or global, to the specified value. At the global level, if the variable is not yet defined, set will define it.
undef
undef(Variable) The most recent global definition of Variable, if any, is removed. The previous definition is restored. The value returned is 1 if there was a definition, 0 otherwise. Caution: This effects globals, even if called inside a function with the same variable bound locally.
repeat
repeat(Value, Command)

does Command the number of times specified by Value.

while
while(Condition, Command)

repeatedly tests Condition and if true executes Command. Returns the value [].

for
for(Initialization, Condition, Incrementation, Command)

does Initialization, then repeatedly tests Condition and if true executes Command, after which Incrementation is executed.

 

Forms (not necessarily functions)
? as in Guard ? Body
is used in guarded rules. Guard is the guard, a logical expression, and Body is an expression. The rule applies only if the guard is true, in which case the result is the value of Body.
 
? as in Condition ? True_Alternative : False_Alternative
 

Condition is evaluated first and if true, the result is the value of True_Alternative, otherwise it is the value of False_Alternative.

 

=> (read "yields") as in Head => Body
is used in two ways: to define rules and to define anonymous functions.

To define a rule:

 

Head => Body;

where Head is a function name with arguments, defines one rule for the named function.

Example: foo([A | L], N) => foo(L, A+N);

On the other hand,

Head => Body 

where Head is just a list of arguments in parentheses, is an "anonymous function" (also known as a "lambda expression").

Example: (F, X) => F(F(X)) is the function of two arguments, say F and X, which applies F to X, then applies F to the resulting value.

 

$ (read "defer") as in $ Expression
 

Evaluation of the expression following the $ operator is deferreduntil the value is actually needed. The definition of "needed" isdependent on the operators which apply to the value.

For example, in the conditional form

C ? F : T, 

if F or T is deferred, then they are leftdeferred in the result. On the other hand, applying the function"first" to the result would evaluate a deferred argument, since itneeds to know the first element of the argument as a list. Theimmediate value of a defer before evaluation is an internalstructure called a seed. While seeds are not seen as output,they may be seen in tracing.

 
Arity Notation
 

Some built-in functions are overloaded. For example,map has two or three arguments. When abuilt-in function is passed as an argument, the ambiguity of whichfunction is to be used can be resolved by appending to the function name a # followed by the arity, e.g. map#2 vs.map#3. The same applies to operators, e.g.+#1 vs. +#2. Some functions can take anarbitrary number of arguments, e.g. max takes any number greater thanzero of arguments. Similarly, + is two differentfunctions: +#1 is the "curried" +, while+#_ is the addition function with any other number ofarguments. If # is not attached, rex will make a choice of arity. Forexample, the binary arithmetic functions are the choice for+, -, *, /, %.

 

Local Definitions and Equational Guards

A local definition takes the form

LHS = RHS, Expression; 

The value of the overall expression is the value ofExpression in the context of the bindings made in the equation. These bindings are local, and hold only for the evaluationof Expression. They have no impact on existing bindings tothe variables. Example:

 root = sqrt(x), f(root, g(root)); 

Here root is bound to the value of sqrt(x), for the evaluation of f(root, g(root)). One use of a localdefinition is to prevent repeated expensive evaluation of anexpression.

When the right-hand side of a rule has this form, we call it anequational guard. The idea is that the rule only applies if thedefinition succeeds. As with global definitions, a definition willfail if there is a mis-match between the LHS and RHS. Example:

 f(x) => root = sqrt(x), g(root,h(root)); 

Here the value of f(x) is computed by binding root tosqrt(x), then returning the value g(root,h(root)). Since root is only a single variable, the pattern istrivial and this guard will never fail. If the evaluation of gfails, this translates to a failure of the whole function f,not to the failure of a guard.

 

Local Recursive Definitions

When an ordinary local definition is made, the values of variablesand functions on the RHS is that determined by the surroundingcontext. Sometimes it is desirable to make those values be exactlythe ones being defined locally. This is particularly true in the caseof defining a recursive function locally. For example, suppose forsome reason we wished to apply a local definition of the factorialfunction. The following would not work:

 f(N) = N < 2 ? 1 : N*f(N-1), f(10);
The reason this doesn't work is that the f on the RHS of the equation (but not the one after the comma) is not the local f, but rather the surrounding f, if indeed there is one. In order to make both f's be the same, so that recursion will work, we need to use a recursive local definition, signified by braces {....}. The proper way to define and use f is:
 { f(N) = N < 2 ? 1 : N*f(N-1); f(10) }; 

In general, the expressions inside braces consist of some number of equations terminated by semi-colon, followed by an expression, the value of which becomes the value of the overall brace expression. Another use of local recursive definitions is in infinite-list functions with loops. For example, to define a function which is the infinite repetition of an item X, without recomputing X, we can define

repeat(X) = { Y = [X |$ Y]; Y }; 

Here it is Y which is the same on both the left- and right-hand sides of the equation Y = [X |$ Y].

 

System Commands

System commands look just like expressions to be evaluated, butthey generally have side-effects, such as reading a rex source file.The "function" sys is thus not really a mathematical function. Thesecond argument to sys is interpreted literally as one of a number ofsub-commands, and there will usually be additional arguments, whichare either literal or evaluated, depending on the sub-command.

The in sub-command:

sys(in, Filename)

Here in is literal and Filename is an expressionwhich must evaluate to a string. That string is looked up accordingto the usual interpretation and loaded as a series of rexexpressions.

The on and off sub-commands:

sys(on, Flag)

sys(off, Flag)

Here on and off are literal and Flag isinterpreted literally (without evaluation). The corresponding flag isturned on or off. The following are possible flags:

The get and set sub-commands:

sys(set, Variable, Value)

sys(get, Variable)

Here set and get are literal. The expressionsets or gets the value of system variable as indicated. Thefollowing system variables aresupported:

Trace Commands

To trace or not trace function entry and exit, use one of

*t
toggles the function trace

 

sys(on, ftrace);
 
turns the trace on

 

sys(off, ftrace);
 
turns the trace off
 
Interrupting rex

To interrupt rex while in evaluation, type control-c once ortwice. You will be offered a menu of possible options, such as:

Interrupt: Please select one of the following: a - abort to top level b - enter break loop c - continue where program left off f - continue with ftrace on h - enter the help loop n - continue with ftrace off s - continue in line-by-line stop mode q or control D - quit the program 

 

Command-Line Arguments

In UNIX implementations, a file name to be read initially as rexsource can be specified on the command line. Example, with thebold-face representing what the user types:

UNIX > rex fac.rex fac.rex loaded 1 rex > fac(10); 3628800 

 

Examples

In addition to the full examples below, see other examples in thetext such as gcd. Below we show the definitionwith a sample interactive execution.

compose composes two unary functions          compose(F, G)(x) = F(G(x));         compose(sq, sq)(4) ==> 256 fibs is a way of defining the Fibonacci sequence with a "loop"         fibs = [1, 1 |$ map(+, fibs, rest(fibs))];        fibs ==> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, ... prod is a parallel product          prod(M, N) => M >= N ? M;         prod(M, N) =>             Median = (M+N)/2,             bottom = sow(prod(M, Median)),  // creates parallel thread            top = prod(Median+1, N),            bottom*top;          prod(1, 10) ==> 3628800        9 threads created, 9 threads completed inner computes the inner product of two lists, using an accumulator          inner(X, Y) = inner_aux(X, Y, 0);         inner_aux([], _, Acc) => Acc;         inner_aux(_, [], Acc) => Acc;         inner_aux([A | X], [B | Y], Acc) => inner_aux(X, Y, Acc+A*B);         inner([1, 2, 3], [4, 5, 6]) ==> 32
Grammar

This is a simplified grammar. Some constraints and nuances are notshown. The following without quotes are meta-symbols: ( ) | -> { }{....} means "any number of repetitions of", including zero. Commentsfollow // to end-of-line.

input_item      -> definition ';'                 |  rule ';'                 |  term ';'  definition      -> head '=' body  head            -> var_head                 |  function_head function_head   -> identifier var_list { '(' var_list ')' }  var_head        -> identifier                 |  '[' var_head_list ']'  var_list        -> empty | identifier { ',' identifier }  var_head_list   -> var_head { ',' var_head }                 |  var_head { ',' var_head } '|' var_head  rule            -> match_head '=>' body  match_head      -> identifier '(' match_list ')'  match_list      -> empty | match_term { ',' match_term }  match_term      -> identifier                 |  constant                              |  identifier '+' numeric_constant                 |  '[' match_list ']'                 |  '[' match_list '|' match_term ']'  body            -> term '?' term                   // guard                 |  { definition ',' } term         // equational guard term            -> or_exp '?' term ':' term                 |  or_exp or_exp          -> and_exp { '||' and_exp } and_exp         -> not_exp { '&&' not_exp } not_exp         -> { '!' } rel_exp rel_exp         -> add_exp comp_op add_exp comp_op         -> '!=' | '>=' | '>' | '==' | '<=' | '<' add_exp         -> mul_exp { add_op mul_exp }        add_op          -> '+' | '-' mul_exp         -> un_exp { mul_op un_exp } mul_op          -> '*' | '/' | '%' un_exp          -> { '-' | '$' } primary  primary         -> constant                 |  '(' term ')'                 |  '[' list ']'                 |  '[' list | term ']'                 |  identifier { '(' list ')' }  // variable or function application  list            -> empty                  |  term { ',' term }  constant        -> numeric_constant                 |  string_constant                 |  char_constant  string_constant -> '"' { char } '"'                // e.g. "a123" char_constant   -> ''' char '''                    // e.g. 'a' or '\n' numeric_constant -> sign digits exponent                 |  sign digits '.' exponent                 |  sign '.' digits exponent                 |  sign digits '.' digits exponent sign            -> '+' | '-' | empty               // optional sign exponent        -> ( 'e' | 'E' ) sign digits                             |  empty                           // optional exponent digits          -> digit { digit } identifier      -> letter { letter | digit } letter          -> 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'                  |'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'                  |'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'                  |'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'                  |'_' digit           -> '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'  char            -> any ascii character                 |  '\' any ascii character         // escape sequence empty           ->                                 // the empty string

 

Alphabetic Listing of all Functions
RCS ID: $Id: rex.html,v 1.29 1996/06/25 19:09:14 keller Exp keller $