In addition to dealing with the assignment statement here, we will also go over the rules governing expressions in EXALT.
The LET or assignment statement has the form
LET variable/variable...=expression; error-clause,error-clause...
where variable can be a simple variable, an array element, or a whole array.
Recommended @-comments, which may also be used as &'...' replacements:
LET var @AND/ var @EQUAL= expression @UPON; error-clause, error-clause
Array subscripts can be full general expressions, and are enclosed by the same normal parentheses as function arguments. Both some predefined functions, and user-defined 'left-hand functions', may also be the object of an assignment.
A LET statement may also assign a value to two or more variables. Such a statement might look like this:
The = sign can also be used as a relational operator; that is, A=B is a valid expression, returning a result of type LOGICAL. Thus,
does not assign C to B and A; instead, it assigns either _T or _F to A depending on whether or not B and C are equal.
A cautionary example of how this can possibly lead to confusion is the statement
which assigns either _T or _F to A and B depending on whether or not C is equal to D divided by E.
For this reason, in FORTRAN, a multiple assignment statement uses the comma instead of the equals sign to separate all left-hand assignment arguments. The slash (/), which is also a symbol found within expressions (it expresses division) is used instead in EXALT to allow assignments, even multiple assignments, to be used as arguments to subprograms. Since the comma is a possible argument separator, using it for this purpose would preclude that.
In addition to allowing the slash or diaresis to be used on the left side of an equals sign, the plus sign is also allowed on the left side of the equals sign, to permit statements like this:
DIM ARRAY(3) ... LET A+B+C=ARRAY
which allows the elements of the array ARRAY to be individually assigned to the variables A, B, and C, making the assignment statement equivalent to the three assignment statements
LET A=ARRAY(1) LET B=ARRAY(2) LET C=ARRAY(3)
A similar form of component-wise assignment is present in Python. When a multi-dimensional array is assinged, parentheses are required when more than one level of the structure is broken up; for example:
DIM SOURCE(2,3), TARGET(2,3) DIM P(3),Q(3) ... TARGET/(A+B+C)+(D+E+F)/P+Q=SOURCE
performs the assignments
LET P=SOURCE(1,) LET Q=SOURCE(2,) LET A=SOURCE(1,1) LET B=SOURCE(1,2) LET C=SOURCE(1,3) LET D=SOURCE(2,1) LET E=SOURCE(2,2) LET F=SOURCE(2,3) LET TARGET=SOURCE
Again, in a multiple assignment statement, all assignments are directly from the expression on the right of the equals sign, so no type conversions performed for any one assignment affect other assignments.
The symbol ?= indicates an assignment operator that always performs assignment, and is never a relational operator. After a ?=, equals signs are interpreted as being relational in the remainder of an assignment statement just as they would after the first =. An expression, the last term in which is a variable, can precede a ?= (provided it is not the first assignment operator, ?= or first =, in the assignment) and the last term is the recipient of the assignment. That is,
is equivalent to
. B=C . A=1+B
as is the case with the distinctive assignment operators used in languages such as APL, ALGOL, Pascal, and C.
In the assignment statement
note that ?= acts on J on its left side, and L+M on its right side. Thus, ?=, if we think of it as having priority, has priority lower than + on the right side, but priority higher than * on the left side. On the other hand,
assigns 5 to V(N), not to N. Thus, array subscripting and, towards the left, the assignment operator, can be considered to have very high priorities, and the assignment operator can be considered to have a very low priority towards the right.
While / and = have different meanings at the start of an assignment statement, ?= has its usual meaning, except that it also indicates the end of the unusual start of the assignment statement. Thus, ?= can replace the first = in an assignment statement. User-defined operators also have their usual meanings at this point, subject to the same qualification as for ?= . Thus, a normal user-defined operator would lead to a statement that calculated, but didn't store the result, but user-defined assigment operators become able to be used here.
This also applies to the other built-in assignment operator, the operator/function &A(op) .
Another way to store intermediate results in an assignment is through the use of argument brackets within an expression. The statement
is equivalent to either
. P=A*B . R=P+C
Note that the second form would not be correct if the + sign were replaced by a - sign. In addition to this, the use of argument brackets within an expression provides additional flexibility by allowing the secondary result of an operator to be stored. This is done by placing a second argument within the square brackets.
Two operators have secondary results. The division operator, /, has as its secondary result the remainder of the division, provided that both of the arguments of the division are of an INTEGER type. The &ANG operator has the square root of the sum of the squares of its arguments as its secondary result.
Thus, a conversion from rectangular to polar coordinates can be expressed as:
. THETA=X &ANG[,R] Y
Note that a space is not required to separate ANG from [, although other optional spaces are shown in the assignment for clarity.
Incidentally, the behavior of division and remainder, when integers can be both positive and negative, is as follows:
A B A/B remainder 11 5 2 1 -11 5 -2 -1 11 -5 -2 1 -11 -5 2 -1
Thus, the absolute values of quotient and remainder are the same when the absolute values of the dividend and the divisor are the same; only the signs are changed to make the result consistent.
The error-clauses are usually omitted. An error-clause has the form
condition = alternative
and causes the alternative to be taken when the condition is present. Conditions are:
ERR any error in evaluating the expression FAIL an error, or a negative result to a search, relational or membership operator FXO fixed overflow FLO floating overflow FLU floating underflow
Where a non-error condition is encountered, as with FAIL, the assignment is performed before the alternative is taken.
An expression may be a constant, a variable, a dyadic operator between two expressions, either of which may be in parentheses, a function with expressions as arguments, a unary operator acting on an expression, or an operator-function with expressions in all appropriate locations. This is, of course, a rule with which complicated expressions can be built from constants and variables.
A variable can be an array element. A constant can be an expression in itself, so it might be better to refer to simple constants. A simple constant is a sequence of digits, or a decimal point either preceded or followed or both by a sequence of digits, a character string in quotes, or an entity formed by use of the % symbol. Other constants are simply expressions which include no variables but which include at least one simple constant.
Operators are performed from left to right in an expression unless they differ in priority, higher priority operators being performed first. The elementary operators in EXALT, grouped according to priority, separated by commas, and preceded by their numerical priority levels, are:
|9000)||left side of ?=, &A.(...)|
|200)||# (or &E), unary -, +, !, &COMP, postfix &DEG, *, &I, &J, &K, +, ~ (or &C)|
|195)||&DIM, &CT, &EX, &REV, &ROT, &TPS, &MTP, ?-, &CA.(...), &APA|
|190)||^ (or &PW), &RT|
|185)||&LOG, &ANG, dyadic and postfix !|
|180)||* (or &TIMES), / (or &OVER), &MUL (or ?L*), &DIV (or ?L/), &INTER, &SHR, &SHL, &ROR, &ROL, &IP.(...), &OP.(...), &PP.(...), ?*|
|175)||(default priority for user operators)|
|170)||+ (or &PLUS), - (or &MINUS), &ADD (or ?L+), &SUB (or ?L-), &UNION, &DISJ, ?+|
|125)||< (or <), > (or >), = (or &EQ), &NE, &LE, &GE, &IN, &CE, &SUBS, &PSUBS|
|120)||unary ~ (or &NOT)|
|-9000)||right side of ?=, &A.(...)|
Expressions as well as arrays can be subscripted by being followed by an expression or series of expressions in parentheses without an intervening operator.
Many of these operators perform familiar operations. Those that may not be obvious are described below:
|a * b||is, of course, a times b, but|
|z *||is the complex conjugate of z: if z is a quaternion of the form a+bi+cj+dk, then the conventional quaternion conjugate of z is used; if z=a+bi+cj+dk, this is a-bi-cj-dk; unlike complex conjugate on the complex numbers, or postfix + and ~ on quaternions, quaternion conjugate is not an automorphism of the quaternions; that is, taking the quaternion conjugate of every element in an equation may alter its validity for reasons not involving principal value conventions. The mapping does have the property that z * (z *) and z + (z *) are real.|
|z +||for z a quaternion of the form a+bi+cj+dk, a+ is a+di+bj+ck. This will produce quaternions from complex numbers.|
|z ~||for z=a+bi+cj+dk, z ~ is a-bi+dj+ck.|
|a # b||is a times 10 to the b-th power, as previously seen. While 3&E5 is a legal constant, as the digit 5 terminates E without being included in the token, X#Y must be replaced with something like X&E Y with at least one space.|
|a &DEG||is a times pi/180, that is, a converted from degrees to radians|
|a &LOG b||is the logarithm of b to the base a|
|a &RT b||is the a-th root of b|
|&RT b||is the square root of b (also _SQR(b))|
|a &ANG b||is the angle, in radians, that a line from the origin to the point (b,a) (a as y-coordinate, b as x-coordinate) would make with the positive-going portion of the x axis. Thus, if both b is positive, a &ANG b is the same as _ATAN(a/b), but otherwise the result will differ in order to place the angle in the correct quadrant. This is the same as _ATAN2(a,b), which also exists in FORTRAN (without, of course, the underscore).|
|a &SHR b||b shifted right by a bits|
|a &SHL b||b shifted left by a bits (equals -a &SHR b)|
|a &ROR b||b rotated right by a bits|
|a &ROL b||b rotated left by a bits (equals -a &ROR b)|
|&DIM a||The dimension of a; if a is multi-dimensional, a vector containing the successive dimensions of a along each axis. Thus, &DIM &DIM a is the order of a; 3 if a is 3-dimensional. (i.e. if M is declared by DIM M(7,4,5) then &DIM M is _A(7,4,5), and &DIM of that is 3.)|
|a &DIM b||Restructure of b; in the example above, _A(4,3) &DIM ?- b would be b; that is, the elements of b are organized into the dimensions specified by the elements of a.|
|a &CT b||Contract; a is an array of type LOGICAL, and b is an array of similar dimensions of any type: the result is an array having the same number of elements as a had elements with the value _T, containing those elements of b which corresponded to the elements of a that were _T, in the same order.|
|a &EX b||Expand; a is an array of type LOGICAL, and b is an array of any type which has as many elements as a has elements with the value _T. The result is an array having the same dimensions as a, containing the elements of b in positions corresponding to the _T elements of a, and "null" elements in the other positions: 0 if b is numeric, _F if b is LOGICAL, blanks if b is CHARACTER, strings of zero length if b is string (including FIXED_STRING), empty lists or sets for those types.|
|a &EN b||Where a and b are n-element arrays, the result of a &EN b is the single number b(n)+a(n)*b(n-1)+a(n)*a(n-1)*b(n-2)+... +a(n)*a(n-1)*...*a(2)*b(1). If a were an n-1 element array, such that newA(i)=oldA(i+1), the result of a &EN b would be the same; a &EN b has a valid result provided that, if b is an n-element array, a is an (n-1)-element array or larger, the earlier elements in a not being used.|
|a &DE b||b is a single number, a an n-element array; then, a &DE b satisfies the condition that a &EN (a &DE b) equals b, and that the last element of a &DE b has its lowest possible positive value, and the second last element of a &DE b has the lowest possible positive value subject to the condition on the last element, and so on.|
|a ?+ b||?+ is the string catenation operator: "ABC" ?+ "DEFG" is "ABCDEFG"|
|?+ a||is an array of single characters, containing
the characters of each element of a in order.
Thus, ?+ "HELLO" is _A("H","E","L","L","O"); when applied to an array, it is applied to each element individually; ?- ?+ a is required to generate a one-dimensional array of characters from any array of strings or characters. This operator does not create arrays of type MUTABLE from other objects; an array of strings containing strings of different lengths has its members all padded with trailing blanks to match the length of the longest string in the array before conversion to array form takes place.
Note that while + is the opposite of -, dyadic ?+ and ?- are the same operation on different domains, and unary ?+ and ?- are opposites in one sense, as ?+ adds a dimension, and ?- removes all dimensions after the first, but they are similar in both creating one-dimensional arrays from other objects.
|a ?- b||?- is the one-dimensional array catenation operator: _A(5,7,3) ?- _A(2,6) is _A(5,7,3,2,6).|
|?- a||is a one-dimensional array having the same
elements as a, in the same logical order. Thus,
if Q were a 3 by 4 array, ?- Q would be a
12-element one-dimensional array, equal to
This is true whether Q was declared as Q(3,4)
or Q.(3,4); that is, the result depends on the
logical ordering, not the physical order, of
the elements of its argument, and it may
therefore produce a rearranged version of its
Note also that if a is a one-dimensional array of MUTABLE values, the result would still be a one-dimensional array of MUTABLE values: while a two-dimensional array of MUTABLE would be converted, again to a one-dimensional MUTABLE array, ?- does NOT perform the function of the _LINV function.
|a &CA.(n) b||is a catenated to b along dimension n; for example, if a is a 3 by 4 by 5 array, and b is a 3 by 2 by 5 array, then c=a &CA.(2) b is a 3 by 6 by 5 array; a(i,j,k)=c(i,j,k) and b(i,j,k)=c(i,j+4,k). Except for the dimension along which catenation takes place, all other dimensions of a and b must be the same.|
|a &APA b||where a is an array of type MUTABLE, and b is any item, a &APA b is an array of type MUTABLE with one more element than a, the last element containing b as its value, the previous elements matching those of a. Thus, this is the same as a ?- _AM(b).|
|&REV a||an array having the same dimensions as a, but with its elements in reverse order|
|&REV.(n) a||reversal along the n-th dimension of a|
|a &ROT b||an array having the same dimensions as b, which, if called c, satisfies the condition that c(m,...,_MOD(n+a,(&DIM b)(&DIM &DIM b))) equals b(m,...,n)|
|a &ROT.(n) b||array rotation along the n-th dimension of b by a places|
|a &TPS b||where b is a multi-dimensional array, and a is a one-dimensional array having as many
elements as b has dimensions, and whose elements are the first _DIM(b) integers, starting
with 1, but not necessarily in order, then where c=a .TPS b,
a can also be smaller; in that case, the higher dimensions of b are neither affected nor numbered: thus, _A(2,1) &TPS a is the same as &MTP a
|&TPS a||a with its dimensions all reversed in order (thus, if c=&TPS a, c(i,j,k)=a(k,j,i))|
|&MTP a||a with its last two dimensions switched (thus, if c=&MTP a, c(i,j,k)=a(i,k,j))|
|a &IN b||True if an element of the array b equals a, or if a is a member of the SET or GROUP b, or if the value of a is a name in the LIST b.|
|a &CE b||Can a be equal to b? If a and b are sets, this means 'Do a and b have any members in common'; if a and b are PATHs, this means 'Do a and b have any alternate values in common'.|
|a ?/ b||is a PATH having a and b as alternate values; ?/ is the 'composition' operator in SNOBOL.|
|a &IP.(op1,op2) b||is, where a is an q by r array, and b is an p by q array, the p by r array (which we will call c) whose elements c(i,j)=(a(i,1) op2 b(1,j)) op1 (a(i,2) op2 b(2,j)) op1 ... (a(i,q) op2 b(q,j)); for example, a &IP.(+,*) b is the matrix product of a and b. Also, op1 and op2 can be functions instead of operators; then, a(i,k) op2 a(k,j) becomes op2(a(i,k),a(k,j)). If op1 is an operator that can have an arbitrarily large number of arguments, a single op1 function is performed on all a(i,k) op2 a(k,j) pairs instead of multiple nested two-argument op1 functions. Note that op2 is always performed before op1, regardless of priority, and that op1 is performed from left to right, NOT from right to left as in APL.|
|a &OP.(op) b||has as its dimension the concatenation of the dimensions of a and b; if c=a &OP.(op) b, then c(i,j,k...p,q,r...)=a(i,j,k...) op b(p,q,r...).|
|a &PP.(op1,op2) b||where a and b are one-dimensional arrays, is a one-dimensional array which has one less than the total number of elements in both a and b; a &PP.(+,*) b performs polynomial multiplication of a and b: in general, if c=a &PP.(op1,op2) b, and there are m elements in a and n elements in b, c(m+n-i-1)=(a(m-i) op2 b(n)) op1 (a(m+1-i) op2 b(n-1)) op1 ... (a(m) op2 b(n-i)) excluding any expressions of the form x op2 y not corresponding to an actual element from 1 to m of a or from 1 to n of b. (thus, c(m+n-1)=a(m) op2 b(n), and c(m+n-(m+n-2)-1)=a(m-(m-1)) op2 b(n-(n-1)).|
|a ?* b||is the matrix product of a and b (also a &IP.(+,*) b).|
|a &I||is a*_I, or a times the constant i, the principal value of the square root of minus one|
|a &J||is a*_J, where J is one of the values of the square root of minus one used in quaternion-valued numbers|
|a &K||is a*_K, where K is another value for the square root of minus one used for quaternions.|
|a !||is a factorial, or, where a is not an integer, _GAMMA(a+1)|
|a ! b||is a!/(((a-b)!)*(b!)) (note the use of parentheses around (a-b)! to indicate that, of the two consecutive operators ! and *, the first was to be considered a postfix operator: the default, when two consecutive operators are found, is always to attempt to interpret the first operator as dyadic and the second as unary, and to report an error if that does not work rather than then trying the other way.)|
&ADD, &SUB, &MUL, and &DIV act upon arrays of integer type numbers (along only the last dimension of any array) to perform multi-precision arithmetic using the array as one large number. Any fixed-point or scale specification applying to the elements of the array is taken to apply to the array as a whole, and thus variables may be declared to have a fixed-point type such that the maximum value of a single variable of that type would be much less than 1, even though this would mean that multiplication of simple variables of such a type would always produce a zero result. (These operators are defined as the operations they represent are most effectively performed using portions of the results of arithmetic operations usually discarded when going from assembly language to a higher-level language, but they are sufficiently specialized that the alternative of defining a type BIGNUM(integer-type) upon which +, -, * and / can act was not considered reasonable: partly because there would be no intention of allowing _SIN, _SQR, let alone _GAMMA, to act on such variables.)
|a &UNION b is the set or group containing everything in a or b|
|a &INTER b is the set or group containing everything in both a and b|
|a &DISJ b is the set or group containing everything in either a or b but not both, their disjunction (to determine if a and b are disjoint, use &NOT a &CE b); this operation may also be used with LISTs and VLISTs|
|a &SUBS b is _T if every element of a is also in b, but not otherwise|
|a &PSUBS b is _T if a &SUBS b is true and if a and b are not identical (a &NE b will test for whether or not a and b are identical)|
|a ?. b is a; this 'operator' is used to piggyback an independent assignment on the end of an expression, as the diamond is used in some versions of APL.|
|a ?= b has the value of b after being converted to the type of a, but it also has the result of assigning the value of b to a.|
|a &A.(op) b has the value of a op b in the type of a, but also has the result of assigning a op b to a.|
. X &A.(+) 1
is equivalent to
which makes &A.(+) equivalent to += in the language C.
Operators and functions are syntactically distinct within the programs that call them, and may also have different calling sequences. Also, arguments may be omitted within function calls. Therefore, it is necessary to indicate explicitly whether a function is being used as an operator or as a function.
Predefined operators are represented by tokens beginning with an ampersand, continuing with the name of the operator. The token is ended by any character other than a letter; if the next thing to follow begins with a letter, a space is needed.
User-defined operators are indicated by preceding their names with an ampersand, then surrounding their names by periods. The trailing period cannot be mistaken for a decimal point as a pair of special characters are required to close the token begun with an ampersand; thus, &.OP.5 is perfectly legal, and operates on five, not one-half.
An operator can have, as its right operand, an expression that happens to begin with a left parenthesis. Thus, operator/functions are distinguished by placing a period between the operator and the left parenthesis following it which begins the argument list to join the parenthesized clause to the operator.
To use a function as an operator is possible in two ways; through use of the ALIAS statement, which will be described considerably later in this manual, or by placing its name between colons following an ampersand. This applies to built-in functions as well, and the leading underscore is a part of the function's name in this case. Note that the use of colons (which are separators) following the ampersand does not indicate that the resulting token is a separator, unlike the case for tokens which begin with a question mark.
In terms of the calling sequence for operators created from functions, a &:op: b is equivalent to op(a,b) and a &:op:.(x...) b is equivalent to op(x...,a,b) whatever separators may be used within the area x... . The default priority for operators is 95, which applies to all user-defined operators unless explicitly declared, and to all built-in functions other than those given in the list of operators above.
Also, &:op: a is equivalent to op(a) while b &:op: is equivalent to op(,b), not the other way around.
For user-defined operators and operators created from functions, an explicit priority can be indicated by placing the number, (if unsigned) immediately after the ampersand. As 95 is the default priority, this means that &95.op. is equivalent to &.op.*nbsp;, and &95:op: is equivalent to &:op: .
Note that AND and OR do not have the same priority, but OR and XOR do. Also, the logical operators can act on INTEGER and UNSIGNED type operands, performing bitwise operations in that case. This fact does not constrain the internal representation of LOGICAL constants; for example, the bit pattern 00000001 might correspond to _T. Thus, &NOT K, where K is of type LOGICAL, and has the value _T with the above representation, could produce a result with the internal representation 00000000, whereas if K were of BYTE type, the result would have the representation 11111110.
These operators can act on arrays, producing array-valued results, as well as on simple variables.
For example, the program
INTEGER A(3)/5,6,7/,B(3)/2,3,4/,C(3) . C=A*B 7 FOR I=1,3 PRINT C(I) REPEAT 7 END
would produce the output
10 18 28
An array-valued constant can be generated with the function _A, which produces an array containing its arguments. Thus, B above could have been replaced by _A(2,3,4).
The built-in operators and functions of EXALT, if they perform a function upon a single number or string or other object, normally perform the same function upon each element of an array if given arrays as their arguments; if they perform a function upon an array of n dimensions, given an array with more dimensions, each subarray corresponding to a value of the previous dimensions is affected by that function, producing a part of the array result. However, there are some exceptions: functions such as _IF and _CASE, and operators that affect multi-dimensional arrays in special ways relating to their dimensionality, such as .RH .
Many functions are built into the EXALT language. Here is a list of some of them:
|Component selection, absolute value, signum, and modulo functions|
|_ABS(x)||absolute value of x|
|_SGN(x)||-1 if x is negative, 1 if x is positive, 0 if x is zero|
|_POS(x)||-1 if x is negative, 1 otherwise|
|_INT(x)||integer part of x (_INT(3.7) is 3, and _INT(-3.7) is -3)|
|_PINT(x)||largest integer less than or equal to x (_PINT(3.7) is 3, _PINT(-3.7) is -4.)|
|_ROUND(x)||if x+.5 is not an integer, _PINT(x+.5); if x+.5 is an integer, the result may be either x+.5 or x-.5|
|_MAX(x,y)||Larger of x and y (any number of arguments)|
|_MIN(x,y)||Smaller of x and y (any number of arguments)|
|_DIMI(x,y)||If x-y is positive, x-y; otherwise, zero|
|_RE(x)||the real part of a complex number|
|_IM(x)||the imaginary part of a complex number (where a complex number refers to a physical quantity, the real part gives the amount of that quantity, and the imaginary part to something else: i.e., to magnetism instead of electricity, to time instead of space; thus the term 'imaginary' is not entirely undeserved)|
|_JQ(x)||the real coefficient of j in a quaternion|
|_KQ(x)||the real coefficient of k in a quaternion|
|_QIM(x)||equal to x with its real part made zero (useful where quaternion arithmetic is used to find the cross product of vectors)|
|_RABS(x)||x, with its real part replaced by its absolute value (unlike _ABS(x), which is the square root of the sum of the squares of the real and imaginary parts of x if x is complex); this is useful in deriving the principal values of some functions|
|_IABS(x)||x, with its imaginary part replaced by its absolute value|
|_JABS(x)||x, with the coefficient of j replaced by its absolute value|
|_KABS(x)||x, with the coefficient of k replaced by its absolute value|
|_MOD(x,y)||x modulo y (_INT(x) matches _MOD(x,1.)): if x is complex, only the real part of x is affected by this function.|
|_IMOD(x,y)||x, with its imaginary part replaced by itself modulo the real number y|
|_JMOD(x,y)||x, with the coefficient of j in x replaced by itself mod y|
|_KMOD(x,y)||x, with the coefficient of k in x replaced by itself mod y|
|_SQR(x)||square root of x|
|_QBR(x)||cube root of x|
|Elementary Transcendental Functions|
|_SIN(x)||sine of x, where x is in radians|
|_COS(x)||cosine of x, where x is in radians|
|_TAN(x)||tangent of x, where x is in radians|
|_COT(x)||cotangent of x, where x is in radians|
|_ARSIN(x)||arcsine, in radians, of x|
|_ARCOS(x)||arccosine, in radians, of x|
|_ATAN(x)||arctangent, in radians, of x|
|_ACOT(x)||arccotangent, in radians, of x|
|_LOG(x)||common (base-10) logarithm of x|
|_LN(x)||natural logarithm of x|
|_EXP(x)||e (2.71828...) to the power of x|
|_SINH(x)||hyperbolic sine of x|
|_COSH(x)||hyperbolic cosine of x|
|_TANH(x)||hyperbolic tangent of x|
|_COTH(x)||hyperbolic cotangent of x|
|_ARSINH(x)||inverse hyperbolic sine of x|
|_ARCOSH(x)||inverse hyperbolic cosine of x|
|_ATANH(x)||inverse hyperbolic tangent of x|
|_ACOTH(x)||inverse hyperbolic cotangent of x|
|_GD(x)||_ATAN(_SINH(x)), the Gudermannian of x (also equals _ASIN(_TANH(x)).)|
|_IG(x)||_ATANH(_SIN(x)) or _ASINH(_TAN(x)), the inverse Gudermannian|
|The Gamma Function|
|_GAMMA(x)||Gamma x (x-1 factorial for integer x) (underscore followed by capital Greek gamma may also be used; the Greek letter gamma by itself, if available, is permitted to have the status of an ordinary letter)|
|_LGAMA(x)||natural logarithm of Gamma x|
|_GAMMA(a,x)||incomplete gamma function|
|_J(n,x)||Bessel function of the n-th order of x (n may be a REAL quantity, that is, a fraction)|
|_JJ(n,x)||Bessel function for spherical co-ordinates (JJ representing italic J)|
|_I(n,x)||Hyperbolic Bessel function|
|_HEXP(n,z)||Hankel function of e^z. When called with a complex second argument, this function is not periodic along the imaginary axis, but instead is continuous, thus allowing winding number to be preserved.|
|Elliptic Integrals of the First Kind and their Inverses|
|_K(k)||complete elliptic integral of the first kind _F(k,_PI/2.)|
|_F(k,ph)||incomplete elliptic integral of the first kind, where k is the modulus, and ph the amplitude (integral, from 0 to ph, of 1/sqr(1-k^2*(sin(q))^2, dq)|
|_F(ph|m)||_F(_SQR(m),ph), or F(ph|m), where ph is the amplitude, m is the parameter (the modulus squared)|
|_F(ph\al)||_F(_SIN(al),ph), or F(ph\al), where ph is the amplitude, al the modular angle|
|_F(ph)||_F(k,ph) where the value of k was previously set using the _SETK(k), _SETM(m), or _SALPHA(al) built-in subroutines.|
|_AM(k,u)||Inverse elliptic integral of the first kind: where u=_F(k,ph), ph=_AM(k,u)|
|_AM(u)||_AM(k,u) with k previously set as for _F(ph)|
|_SN(u)||_SN(k,u) with k previously set as for _F(ph)|
|_CN(u)||_CN(k,u) with k previously set as for _F(ph)|
|_TN(k,u)||_TAN(_AM(k,u)) also _SC(k,u), see below|
|_TN(u)||_TN(k,u) with k previously set as for _F(ph)|
|_DN(u)||_DN(k,u) with k previously set as for _F(ph)|
|_NS(...)||1./_SN(...) also CSCN, where ... may be any of the four possible sequences of arguments for _SN as shown above|
|_NC(...)||1./_CN(...) also SCN|
|_SC(...)||_SN(...)/_CN(...) also TN|
|_CS(...)||_CN(...)/_SN(...) also CTN|
|Elliptic Integrals of the Second Kind and their Inverses|
|_E(k)||complete elliptic integral of the second kind _E(k,_PI/2.)|
|_E(k,ph)||incomplete elliptic integral of the second kind (integral, from 0 to ph, of sqr(1-k^2*(sin(q))^2), dq)|
|_E(u|m)||_E(k,ph) where k=sqr(m) and ph=_AF(k,u)|
|_E(ph\al)||_E(k,ph) where k=sin(al)|
|_IE(ph)||_E(k,ph) using a pre-set value of k|
|_Z(k,ph)||_E(k,ph)-(_E(k)*_F(k,ph))/_K(k), Jacobi's Zeta function|
|Elliptic Integrals of the Third Kind and their Inverses|
|_PI(k,n,ph)||incomplete elliptic integral of the third kind (integral, from 0 to ph, of sqr(1.-k^2*(sin(q))^2)/(1.-n*(sin(q))^2),dq)|
|_PI(n,ph)||_PI(k,n,ph) using a pre-set value of k|
|_PI(n;u)||_PI(k,n,_AM(k,u)) using a pre-set value of k|
|Various Special Functions|
|_SI(x)||Sine integral (integral, from 0 to x, of sin(v)/v, dv)|
|_L(n,x)||Modified Struve function|
|_ANJ(n,x)||Anger's function (also boldface J)|
|_WBE(n,x)||Weber's function (also boldface E)|
|_ERFC(x)||Complementary error function (1-_ERF(x))|
|_ZETA(x)||Riemann zeta function|
|_LAMBDA(ph\al)||Heuman's Lambda function|
|_M(a,b,x)||Kummer's first confluent hypergeometric function|
|_U(a,b,x)||Kummer's second confluent hypergeometric function|
|_M(ka,mu\z)||Whittaker's first confluent hypergeometric function|
|_W(ka,mu\z)||Whittaker's second confluent hypergeometric function|
|_F(a,b;c;z)||Gauss hypergeometric function|
|_F(a,b,c...;r,s,t...;z)||Generalized hypergeometric function (p and q are implicit, determined by the number of arguments between the first two semicolons)|
|_INV(a,b)||the inverse of a, where a is a square matrix (if a is an array of three or more dimensions whose last two dimensions are equal, each square made of the last two dimensions is inverted), where, if b is 1 or omitted, an algorithm that will provide a correct inverse even for extremely ill-conditioned matrices is used, but if b is 2, an algorithm that executes in a reasonable time for large matrices is used.|
|Functions for Conditional Expressions|
|_IF(a,b,c,d)||if a is numeric, b if a is negative, c if a is zero, d if a is positive.|
|_IF(a,b,c)||b if a is _T, c if a is _F|
|_AIF(...)||as _IF, but if the first argument is an array, selects corresponding elements of b, c, and d (if present), and requires that b, c, and d have the same structure, and compatible types and current types (if applicable)|
|_ON(n,a,b,c...;x;p,q,r...;z)||if n is 1,2,3..., the result is a,b,c... respectively; if bigger, x; if 0,-1,-2..., the result is p,q,r... respectively, if smaller, z. Arguments may be omitted, but an error results if no argument is present corresponding to the value of n.|
|_AON(...)||version of _ON allowing element-by-element selection|
|_CASE(a;p:w,q:x...)||w if a=p, x if a=q, and so on. Multiple values leading to a single result can be separated by ?2: which is also a comma within one level of argument brackets ([ and ]). A value after the semicolon linked to another argument by a colon is the result if no match is found.|
|_AOCASE(...)||version of _OCASE allowing element-by-element selection|
|_SELECT(a:w,b:x,c:y...)||one of w,x,y... preceded by an a,b,c... that has the value _T. A value not linked to another argument by a colon is the result if none of the a,b,c... items are true.|
|_OSEL(a:w,b:x,c:y...)||ordered _SELECT; the equivalent of this is used in LISP|
|_AOSEL(...)||version of _OSEL allowing element-by-element selection|
|_SCAN(a,b,c,d;p:w,q:x...)||searching string a, starting from character b, and continuing to use starting positions up to character c, and considering characters up to position d, the one of the values w,x,... corresponding to the one of the strings p,q,... found at the earliest possible position in the string a. Omitting b, c, and d eliminates the constraints to which they refer; an argument after the semicolon not linked to another argument by a colon is the value to be returned if no match is found.|
|_ASCAN(a,b,c,d;p:w,q:x...)||similar to _SCAN, but operates on the elements of arrays rather than the characters of strings for scanning. (This is NOT analogous to _AIF, etc., scanning is still along a single array, instead of a single string, producing a single decision and a single result.)|
|Array- and String- Related Functions|
where lex is omitted, (ex, evaluated with var=s) op (ex, evaluated with var=s+i) op (ex, evaluated with var=s+i+i) op ... (ex, evaluated with var=s+i+i....+i such that var is less than or equal to e, but var+i is not, if i is positive, or, if i is negative, such that var is greater than or equal to e, but var+i is not) with i defaulting to 1 if omitted, and with op performed from left to right, as usual for EXALT.
Thus, _RO(+,I,0,25;(X &PW I)/(I!)) is the sum, from I=0 to 25, of x^i/i!, which happens to be the first 26 terms in the series for e to the x-th power.
Note that var is not a dummy variable, although it performs a similar function; therefore, _RO can be nested (that is, one _RO used within the ex argument of another) and produce the expected result.
When lex is present, e may be omitted, and defaults to infinity having the appropriate sign; terms (or factors, if op is *) in the series continue to be used as long as lex remains true. The niladic function _CTERM has the value, when used within lex, of the value of ex that will be used if lex is true, so it is possible to use _RO in the fashion _RO(+,I,0;(X &PW I)/(I!);_CTERM > .000001) which sums all the terms greater than .000001 in the infinite series expansion for e to the x-th power.
The niladic function _PTERM is also available, so that a test of the ratio between successive terms can be used without resorting to the use of ?. and ?=`.
_PTERM is also useful when iex is present; in that case, when var=s, iex is used instead of ex. Using _PTERM within ex can improve the efficiency of _RO, as the normal expression of series with _RO can be both inefficient and difficult to optimize.
Thus, we can replace _RO(+,I,0,25;(X &PW I)/(I!)) by _RO(+,I,0,25;_PTERM*X/I:1) and obtain improved efficiency in most cases.
With _RO(*,I,0,25;1+(X &PW I)/(I!)), however, _RO(*,I,0,25;1+(_PTERM-1)*X/I;2) while theoretically equivalent, is subject to needless roundoff error; _RO(*,I,0,25;1+OAD?=OAD*X/I:1+OAD?=1), is preferable, and it is required that _RO be implemented so as to allow this formulation to consistently achieve a correct result.
This function produces ex1 op1 (ex2 op2 (ex1 op1 (ex2 ... ))) where each pair of ex1 and ex2 is evaluated with one value of var (as in _RO above), and, despite the fact that the operations are applied in right-to-left order, the expressions are first evaluated in left-to-right order to see how far it is necessary to go.
For op1 being +, and op2 being *, this generates continued fractions.
lex1 and lex2 are provided so that the operation can halt after either operation if desired; if both are omitted, and e is used as a control, the last operation in the expression is always op1, the expressions being in pairs. In addition to _CTERM and _PTERM, _POTERM is available, giving the previous value of the other expression: ex2 or iex2 within ex1 and lex1, ex1 or iex1 within ex2 and lex2. _OTERM, for the value of the other term at the current value of var, is available within ex2 and lex2, but not in ex1 and lex1, as ex1 is calculated before ex2; advance calculation of any term cannot be forced, as this could lead to circular references.
|_CFNI(op1,op2,var,s,e,i;bex1:ex1:iex1,bex2:ex2:iex2;lex1,lex 2)||similar to _CF, but avoids the use of intermediate storage for the expressions, instead evaluating them twice, once on the way down, once on the way back. ex and iex are used on the way down; bex may contain _NTERM and _NOTERM but not _PTERM and _POTERM; if it does, it uses the last value calculated on the way down as its starting point. If bex is generalized, ex and iex can be omitted, but this is usually less efficient, just as iex can be omitted if ex is generalized. (_NTERM is next term, just as _PTERM is previous term; when working backwards, the next term is the term last calculated, and the previous term is not yet available.)|
|_LOC(a,b)||The position of the first occurrence of the string b in the string a. (Sets FAIL if b not in a, and gives the result 0)|
|_ALOC(a,b)||The position of the first occurrence of the element a in the array b. Result is zero, not the size of b plus 1 if a is not in b, as EXALT does not have a settable index origin (also sets FAIL).|
|_SS(a,i,j)||The i-th through j-th characters of a; 0 stands for the last character, -1 the second last, and so on, as well. If a is an array of strings, the appropriate substring of each element of that array.|
|_SA(a;i:j)||Elements i through j of array a, except that, unlike a(i:j), 1 always refers to the first element of array a, 2 to the second, and so on, and therefore 0 can be used to refer to the last element, -1 to the second last, and so on as well.|
|_SA(a;i:j,k,m:n)||aa(i:j,k,m:n) where aa is a converted to have only numerical subscripts starting from 1 and consecutive|
|_SA(a)||aa derived from a, as above|
|_SA(a;i:j:n)||aa(,,,i:j,,,) where the subscript range i:j is applied to the n-th dimension of aa (multiple i:j:n type groups are also permitted)|
|_RPF(a,b,c)||String a after the first occurrence of string b in it has been replaced by string c. (Sets FAIL if b not in a)|
|_RPA(a,b,c)||String a after all occurrences of string b in it have been replaced by string c. (Sets FAIL if b not in a)|
|_TLATE(a,b,c)||String a after every character in string a that is also in string b is replaced by the character in the corresponding position of string c|
|_TTABLE(a,b)||A translate table indicating that all characters in string a have the corresponding characters of string b as their equivalents, all other possible characters having themselves as their equivalents|
|_TLATE(a,b)||_TLATE(a,_TTABLE(b,c)) is the same as _TLATE(a,b,c), but where _TTABLE can be calculated once in advance this may execute more quickly|
|_PFIN(a)||where I is an integer with the value 25, _PFIN(I) is the string "25". The sign of a number is ignored by this function.|
|_DELTA(x)||the smallest positive number that, when added to and subtracted from x, will in both cases cause either a change in the value of x or a floating-point overflow. Note that, due to rounding, adding or subtracting _DELTA(x) to or from x is likely to result, in at least one of the two cases, in a number differing from x by twice _DELTA(x). Also note that the result depends on the type of x.|
|_RES(a,b;x)||Creates an (a,b) dimensioned array containing the elements of x (equivalent to _A(a,b) ?RH. x)|
|_REDUC(op,a)||a(1) op a(2) op a(3) ... op a(n) where a is an array with n elements|
|_REDUC(op,a,n)||op-reduction along the n-th dimension of the array a|
|_PARPRO(op,a)||if c=_PARPRO(op,a), then c(1)=a(1), c(2)=a(1) op a(2), c(3)=a(1) op a(2) op a(3), and so on.|
|_REV(a)||An array with the same elements as a, but in reverse order. For multi-dimensional arrays, reversal takes place along the last dimension only.|
|_REV(n,a)||Reversal along the n-th dimension of array a|
|_IOTA(i)||An array consisting of the numbers 1 through i in order|
|_IOTA(i,j)||An array consisting of the numbers i through j in order|
|_SIZE(a)||The number of default-size characters that could be contained in the storage occupied by the data portion of a (i.e. the number of bytes occupied by the data portion of a on many machines).|
|_LEN(a)||If a is a string or character variable, the number of characters in a; if a is an array of such values, _LEN(a) is an array of numbers, each element in it being the length of the corresponding element of a.|
|_TLEN(a)||_REDUC(+,.DIM a) in all cases.|
|_A(x,y,z)||Creates a one-dimensional array containing the elements x, y, and z|
|_S(a:b,c:d,e)||The SET containing everything from a to b, everything from c to d, and e by itself.|
|_L(a=b,c=d)||The LIST in which 'a' equals b and 'c' equals d are the facts stored (one argument, of type LIST, and attribute LIST_EQUATE, is what this function actually has).|
|_ATL(a,b)||Produces a LIST from the array of names a and the array of values b.|
|_LMAKE(a:p,b:q,c,d,e)||Produces a VLIST where variable a is known under the name p, variable b under the name q, and variables c, d, and e are known under their own names.|
|_AM(a,b,c)||The array of type MUTABLE the first element of which has the value of a, the second of which has the value of b, the third the value of c. This can have any number of arguments, including one. Note that ?- can concatenate two arrays of type MUTABLE to form a longer one, but cannot concatenate anything else to such an array (since elements of an array of type MUTABLE may have arrays of type MUTABLE as their values, allowing concatenating objects to a MUTABLE array would make the concatenation of two MUTABLE arrays ambiguous).|
|_NULL(a)||True if a is an array of type MUTABLE with no elements (equal to _NIL: in EXALT, equality is defined for the empty set and similar objects).|
|_LINV(a)||Where a is an array of type MUTABLE, this returns a result that is also an array of type MUTABLE, but in which any element of a that is also an array of type MUTABLE is replaced by its elements in order. That is, if a=_AM(x,_AM(y,z)), _LINV(a) is _AM(x,y,z), provided that neither x, y, nor z is an array of type MUTABLE.|
|_STRUCT(a)||For a as given above, _STRUCT(a) would be the string "(.(..))". If a is not an array of type MUTABLE, it gives the result "."; thus, "." represents anything that is not an array of type MUTABLE, and a set of parentheses encloses the contents of such an array. Note that an array of type MUTABLE with more than one dimension is considered to be a simple object instead of a MUTABLE array for the purpose of the _LINV, _STRUCT, _SEQ, _AM and related functions.|
If found within the argument list of a subprogram, where that subprogram has no TEXT, EQUATE, or LIST_EQUATE arguments, and is not generated by means of an ALIAS statement, if n=0 it has the following action: it places a as one argument in the argument list of the subprogram in which it appears, unless a is a MUTABLE variable whose value is that of an array of MUTABLE variables. In that case, the elements of a are instead passed as separate arguments, each one separated from its neighbors by commas, unless they, in turn, are arrays of type MUTABLE. In that case, they are passed as successive arguments, but separated by colons; and so on, using separators of successively lower level.
The parameter n simply indicates the starting level for separators; if 1, for example, semicolons rather than commas are used in the beginning. Note that an array of type MUTABLE with one element at any point such a structure does not produce a result distinguishable from the result that that element replacing that array would have produced, since the notation used for function arguments in EXALT is not isomorphic to true list or tree notation.
Note that a function of this sort cannot be created by the user by means of the ALIAS statement or any other mechanism, although a subroutine of the form _SCALL(subr,a) that produces the same result as subr(_SEQ(a)) is quite possible in assembly language.
|_R(a,b)||The SET(type of a and b) which includes all entities of that type between a and b|
|_SPAN(a)||A PATH whose possible values are any string, of any length, composed solely of characters from within a|
|_BREAK(a)||A PATH whose possible values are any string, of any length, composed solely of characters not within a|
|_ES(a,b,c)||Where a is a SET, GROUP, or PATH with a finite number of members, b a dummy variable, c a LOGICAL-valued expression including b, this is the SET, GROUP, or PATH containing the elements of a, or having the possible elements of a, that when assigned to b, make c true. This performs one evaluation of c for every element/possible value of a, since c can be any expression: use .INTER and _R to impose a simple constraint upon a set; this function is for imposing complex constraints. (Use _ to convert to and from PATH type to perform .INTER on PATHs.)|
|_SFC(a)||Converts an array of single characters into a string with those characters in order|
|_AL(a)||Given an integer (including unsigned) argument a, an array of LOGICAL values corresponding to the bits of a, as used by integer .AND, .OR, .XOR, and .NOT operations|
|_IFL(a)||Converts an array of LOGICAL values into an integer made of the corresponding bits. Note that _IFL(_AL(a)) may not necessarily equal a on a decimal machine, or even a one's complement machine, if some bits of a cannot be used in binary manipulations.|
|_CON(a)||Where a is an ADDRESS variable, the contents of that address interpreted according to the associated type of the address|
|_ADR(a)||The address where a is stored (if a is an expression, this will be the address of a temporary location)|
|_(typename,a)||The value a represented in type typename, if possible: thus, _(REAL,5), or _(R,5), is the number 5 in REAL form. This does not convert between pointers and the objects they point to, but it will convert between the types PATH and SET or GROUP.|
|_TERM(d)||_T if device d is a source of interactive input, from the user or from another process, _F if device d is a file, a tape drive, or a source of batch input. Note the description below of the niladic call to _TERM.|
The functions _NUMBER and _PRESENT concern argument passing, which is discussed in the description of the FUNCTION statement below, and other non-arithmetical functions will be dealt with in connection with the appropriate topic.
Functions with no arguments usually represent constants:
but not always:
In addition to non-arithmetical functions, EXALT also posesses pseudofunctions. These have the same syntax as functions, but may only be used within certain types of statements, and take actions which depend on information elsewhere in the statement, producing results which may not belong to any EXALT type.
Most commonly, pseudofunctions are found in input/output statements; the _TAB(position) and _IND(keyname,value) pseudofunctions are examples.
Also, there are some functions in EXALT which can appear on the left-hand side of the equals sign in an assignment statement. Thus, while they have a syntax resembling that of functions, they receive a result rather than producing one. The simpler LEFT_FUNCTIONS in EXALT are the following:
|_CON(a)||The item pointed to by the ADDRESS variable a, having as its type the associated type of a, if one is present.|
|_MEM(a)||Similar to CON, but always typeless.|
|_MID(a,b,c)||The c characters of the string a starting at the b-th character. a may be of type STRING or CHARACTER*n, but MID refers to an entity of CHARACTER type: assignment to MID does not change the length of the string a; instead, that which is assigned is either padded or truncated.|
|_SS(a,b,c)||The b-th through c-th characters of a; also does not cause change of a's length.|
|_AREA(a,b,c)||The c characters of the string a starting at the b-th character, having STRING type. Assignment to AREA will change the length of a if that which is assigned has a length other than c.|
|_SBS(a,b,c)||The b-th through c-th characters of a are replaced by what is assigned to this left-hand function; also may change the length of a. change the length of a.|
|_AO(a,b)||All occurrences of string b in string a are
replaced by that which is assigned to this
function. If a is a path, all occurrences
of b in all possible values of a are changed.
This can change the structure of a: for
. A='EV' ?+ ('AE' ?/ 'RO')gives A the two possible values EVAE and EVRO. If we then execute the statement . _AO(A,'EVA')='MARY' then A is changed to have the two possible values MARYE and EVRO. This will set FAIL if string b does not occur in string a.
|_FO(a,b)||The first occurrence of string b in string a is replaced by that which is assigned to this function. If a is a path, the first occurrence of b in all possible values of a is so altered: this may change the topology of a: for example, . A='G' ?+ ('HI' ?/ 'HO') ?+ 'R' ?+ ('AB' ?/ 'HI') gives A the four possible values GHIRAB, GHIRHI, GHORAB, and GHORHI (remember, ?+ has higher priority than ?/). If we then execute the statement . _FO(A,'HI')='MX' then the four possible values of A become GMXRAB, GMXRHI, GHORAB, and GHORMX. There is no way to substitute MX for all or some of the HI's in the expression which created A to create a path with these possible values, since the second HI is replaced by MX only when the first HI is not used. The new value of A is given by the expression 'G' ?+ ('MXR' ?+ ('AB' ?/ 'HI') ?/ 'HOR' ?+ ('AB' ?/ 'MX')) incidentally. This will set FAIL if string b cannot be found within string a.|
|_SEL(a,b)||The elements of the array a corresponding to elements of the logical array b containing the value _T are to be assigned the values of the elements of the expression on the right-hand side of the equals sign which correspond to them, the others being discarded. (And, perhaps, not even being calculated at all, given a suitably optimizing compiler.)|
|_DMV(a)||The dimensions of a can be assigned a value, provided that a is of type MUTABLE. However, . _DMV(a)=n is not the same as . a=n &DIM a but instead is dangerous, and can result in the mutable variable a including storage which should not belong to it; its purpose is instead to allow the artifical creation of MUTABLE variables referring to specific areas in storage.|