Loosely speaking, an operator is a function, usually
attached to basic arithmetic operations, whose name contains only
nonalphanumeric characters. For instance +
or -
, but also
=
or +=
, or even [ ]
(the selection operator). As all
functions, operators take arguments, and return a value; assignment
operators also have side effects: besides returning a value, they change the
value of some variable.
Each operator has a fixed and unchangeable priority, which means that, in a given expression, the operation with the highest priority is performed first. Unless mentioned otherwise, operators at the same priority level are left-associative (performed from left to right), unless they are assignments, in which case they are right-associative. Anything enclosed between parenthesis is considered a complete subexpression, and is resolved recursively, independently of the surrounding context. For instance,
a + b + c --> (a + b) + c \\ left-associative a = b = c --> a = (b = c) \\ right-associative
Assuming that op1, op2, op3 are
binary operators with increasing priorities (think of +
,
*
, ^
),
x op1 y op2 z op2 x op3 y is
equivalent to x op1 ((y op2 z) op2
(x op3 y)).
GP contains many different operators, either unary (having only
one argument) or binary, plus a few special selection operators. Unary
operators are defined as either prefix or postfix, meaning
that they respectively precede (op x) and follow (x op) their
single argument. Some symbols are syntactically correct in both positions,
like !
, but then represent different operators: the !
symbol
represents the negation and factorial operators when in prefix and postfix
position respectively. Binary operators all use the (infix) syntax
x op y.
Most operators are standard (+
, %
, =
), some are
borrowed from the C language (++
, <<
), and a few are
specific to GP (\
, #
). Beware that some GP operators differ
slightly from their C counterparts. For instance, GP's postfix ++
returns the new value, like the prefix ++
of C, and the binary
shifts <<
, >>
have a priority which is different from (higher
than) that of their C counterparts. When in doubt, just surround everything
by parentheses; besides, your code will be more legible.
Here is the list of available operators, ordered by decreasing
priority, binary and left-associative unless mentioned otherwise. An
expression is an lvalue if something can be assigned to it. (The name
comes from left-value, to the left of a =
operator; e.g.
x
, or v[1]
are lvalues, but x + 1
is not.)
* Priority 14
:
as in x:small
, is used to indicate to the GP2C compiler that the
variable on the left-hand side always contains objects of the type specified
on the right hand-side (here, a small integer) in order to produce more
efficient or more readable C code. This is ignored by GP.
* Priority 13
( )
is the function call operator. If f is a closure and args
is a comma-separated list of arguments (possibly empty),
f(args)
evaluates f on those arguments.
* Priority 12
++
and --
(unary, postfix): if x is an lvalue
,
x++
assigns the value x+1 to x, then returns the new value of
x. This corresponds to the C statement ++x
: there is no prefix
++
operator in GP. x--
does the same with x-1. These
operators are not associative, i.e. x++++
is invalid, since
x++
is not an lvalue.
* Priority 11
.
member (unary, postfix): x.member
extracts
member from structure x (see Section se:member).
[ ]
is the selection operator. x[i]
returns the i-th
component of vector x; x[i,j]
, x[,j]
and
x[i,]
respectively return the entry of coordinates (i,j), the
j-th column, and the i-th row of matrix x. If the assignment operator
( =
) immediately follows a sequence of selections, it assigns its right
hand side to the selected component. E.g x[1][1] = 0
is valid; but
beware that (x[1])[1] = 0
is not (because the parentheses force the
complete evaluation of x[1]
, and the result is not modifiable).
* Priority 10
'
(unary, postfix): derivative with respect to the main variable.
If f is a function (t_CLOSURE
), f' is allowed and defines a new
function, which will perform numerical derivation when evaluated
at a scalar x; this is defined as (f(x+ϵ) - f(x-ϵ)) /
2ϵ for a suitably small epsilon depending on current precision.
? (x^2 + y*x + y^2)' \\ derive with respect to main variablex
%1 = 2*x + y ? SIN = cos' %2 = cos' ? SIN(Pi/6) \\ numerical derivation %3 = -0.5000000000000000000000000000 ? cos'(Pi/6) \\ works directly: no need for intermediateSIN
%4 = -0.5000000000000000000000000000
~
(unary, postfix): vector/matrix transpose.
!
(unary, postfix): factorial. x!
= x(x-1)...1.
#
(unary, postfix): primorial. For a non-negative integer n, n#
is the product of all prime numbers less than or equal to n.
* Priority 9
#
(unary, prefix): cardinality; #x
returns length(x)
.
!
(unary, prefix): logical not. !x
returns 1 if x is
equal to 0 (specifically, if gequal0(x) == 1
), and 0 otherwise.
* Priority 8
^
: powering. This operator is right associative:
2^3^4
is understood as 2^(3^4)
.
* Priority 7
+
, -
(unary, prefix): -
toggles the sign of its argument,
+
has no effect whatsoever.
* Priority 6
*
: multiplication.
/
: exact division (3/2
yields 3/2, not 1.5).
\
, %
: Euclidean quotient and remainder, i.e. if x =
qy + r, then x
= q, \
yx%y
= r. If x and y
are scalars, then q is an integer and r satisfies 0 ≤ r < |y|; if x
and y are polynomials, then q and r are polynomials such that deg r <
deg y and the leading terms of r and x have the same sign.
\/
: rounded Euclidean quotient for integers (rounded towards
+ oo when the exact quotient would be a half-integer).
<<
, >>
: left and right binary shift. By definition,
x << n
= x * 2^n if n > 0, and truncate
(x 2-n) otherwise.
Right shift is defined by x >> n
= x << (-n)
.
* Priority 5
+
, -
: addition/subtraction.
* Priority 4
<
, >
, <=
, >=
: the usual comparison operators,
returning 1 for true
and 0 for false
. For instance,
x <= 1
returns 1 if x ≤ 1 and 0 otherwise.
<>
, ! =
: test for (exact) inequality.
==
: test for (exact) equality.
===
: test whether two objects are identical component-wise. This is
stricter than ==
: for instance, the integer 0, a 0 polynomial or a
vector with 0 entries, are all tested equal by ==
, but they are not
identical.
* Priority 3
&&
: logical and.
||
: logical (inclusive) or. Any sequence of logical
or and and operations is evaluated from left to right,
and aborted as soon as the final truth value is known. Thus, for instance,
x == 0 || test(1/x)
will never produce an error since test(1/x)
is not even evaluated
when the first test is true (hence the final truth value is true). Similarly
type(p) == "t_INT" && isprime(p)
does not evaluate isprime(p)
if p
is not an integer.
* Priority 2
=
(assignment, lvalue =
expr). The result of
x = y
is the value of the expression y, which is also assigned to
the variable x
. This assignment operator is right-associative. This is
not the equality test operator; a statement like x = 1
is always
true (i.e. nonzero), and sets x
to 1; the equality test would be
x == 1
. The right hand side of the assignment operator is evaluated
before the left hand side.
It is crucial that the left hand-side be an lvalue there, it avoids
ambiguities in expressions like 1 + x = 1
. The latter evaluates as
1 + (x = 1)
, not as (1 + x) = 1
, even though the priority of
=
is lower than the priority of +
: 1 + x
is not an lvalue.
If the expression cannot be parsed in a way where the left hand side is an lvalue, raise an error.
? x + 1 = 1 *** syntax error, unexpected '=', expecting $end or ';': x+1=1 *** ^--
Assignment to all variables is a deep copy: after x = y, modifying a component of y will not change x. To globals it is a full copy to the heap. Space used by local objects in local variables is released when they go out of scope or when the value changes in local scope. Assigning a value to a vector or matrix entry allocates room for that entry only (on the heap).
op =
, where op is any binary operator
among +
, -
, *
, %
, /
, \
, \/
,
<<
, or
>>
(composed assignment lvalue op =
expr).
The expression x op = y
assigns (x
op y)
to x
, and returns the new value of x
. The result is not
an lvalue; thus
(x += 2) = 3
is invalid. These assignment operators are right-associative:
? x = 'x; x += x *= 2 %1 = 3*x
* Priority 1
- >
(function definition): (vars)- > expr
returns a
function object, of type t_CLOSURE
.
Remark. Use the op =
operators as often as possible
since they make complex assignments more legible. Compare
v[i+j-1] = v[i+j-1] + 1 --> v[i+j-1]++ M[i,i+j] = M[i,i+j] * 2 --> M[i,i+j] *= 2
Remark about efficiency. the operators ++
and --
are usually a little more efficient than their expended
counterpart:
? N = 10^7; ? i = 0; for(k = 1, N, i=i+1) time = 949 ms. ? i = 0; for(k = 1, N, i++) time = 933 ms.
On the other hand, this is not the case for the
op =
operators which may even be a little less efficient:
? i = 0; for(k = 1, N, i=i+10) time = 949 ms. ? i = 0; for(k = 1, N, i+=10) time = 1,064 ms.