To make it easier to write Pike, and to make the code somewhat shorter, some functions can be called with just one or two characters in the code. These functions are called operators and we have already seen how they work in plenty of examples. In this chapter I will describe in detail what they do. The operators are divided into categories depending on their function, but beware that some operators have meanings that go way beyond the scope of the category they are in.
The arithmetic operators are the simplest ones, since they work just like you remember from math in school. The arithmetic operators are:
Function | Syntax | Identifier | Returns |
Addition | a + b | `+ | the sum of a and b |
Subtraction | a - b | `- | b subtracted from a |
Negation | - a | `- | minus a |
Multiplication | a * b | `* | a multiplied by b |
Division | a / b | `/ | a divided by b |
Modulo | a % b | `% | the remainder of a division between a and b |
Exponentiation | a ** b | `** | the result of raising a to the b:th power |
The third column, "Identifier" is the name of the function that actually
evaluates the operation. For instance, a + b
can also be written
as `+(a, b)
. I will show you how useful this can be at the end
of this chapter.
When applied to integers or floats these operators do exactly what they
are supposed to do. The only operator in the list not known from basic
math is the modulo operator. The modulo operator returns the
remainder from an integer division. It is the same as calculating
a - floor(a / b) * b
. floor
rounds the value down to
closest lower integer value. Note that the call to floor
isn't
needed when operating on integers, since dividing two integers will return
the result as an integer and it is always rounded down. For instance,
8 / 3
would return 2
.
If all arguments to the operator are integers, the result will also be an integer. If one is a float and the other is an integer, the result will be a float. If both arguments are float, the result will of course be a float.
However, there are more types in Pike than integers and floats. Here is the complete list of combinations of types you can use with these operators:
Operation | Returned type | Returned value |
| int | the sum of the two values |
| float | the sum of the two values |
| string | In this case, any int or float is first converted to a string. Then the two strings are concatenated and the resulting string is returned. |
| array | The two arrays are concatenated into a new array and that new array is returned. |
| mapping | A mapping with all the index-value pairs from both mappings is returned. If an index is present in both mappings the index-value pair from the right mapping will be used. |
| multiset | A multiset with all the indices from both multisets is returned. |
| int | The right value subtracted from the left. |
| float | The right value subtracted from the left. |
| string | A copy of the left string with all occurrences of the right string removed. |
| array | A copy of the left array with all elements present in the right array
removed. Example: ({2,1,4,5,3,6,7}) - ({3,5,1}) will return
({2,4,6,7}) . |
| mapping | A new mapping with all index-value pairs from the left mapping, except those indices that are also present in the right mapping. |
| multiset | A copy of the left multiset without any index present in the left multiset. |
| int | Same as 0 - int . |
| float | Same as 0 - float . |
| int | the product of the two values |
| float | the product of the two values |
| string | All the strings in the array are concatenated with the string on the
right in between each string. Example: ({"foo","bar"})*"-"
will return "foo-bar" . |
| array | All the arrays in the left array are concatenated with the array on
the right in between each array. Example: ({ ({"foo"}) ,({"bar"})})*({"-"})
will return ({ "foo","-","bar" }) . |
| string | This operation will concatenate the string N times. Example:
"foo"*3 will return "foofoofoo" . |
| string | This operation will concatenate the string X times. Example:
"foo"*2.5 will return "foofoofo" . |
| string | This operation will concatenate the array N times. Example:
({"foo"})*3 will return ({"foo","foo","foo"}) . |
| string | This operation will concatenate the array X times. Example:
({1,2,3})*2.5 will return ({1,2,3,1,2,3,1,2}) . |
| int | The right integer divided by the left integer rounded towards minus infinity. |
| float | The right value divided by the left value. |
| array(string) | In symmetry with the multiplication operator, the division operator
can split a string into pieces. The right string will be split at every
occurrence of the right string and an array containing the results will
be returned. Example: "foo-bar"/"-" will return
({"foo","bar"}) |
| array(string) | This will split the string into pieces. The size of the pieces is
given by the integer. Only complete pieces will be included in the result,
the 'reminder' is discarded. Example: "foo-bar"/2 will
return ({"fo","o-","ba"}) |
| array(string) | This is similar to dividing a string with an integer, but it allows
fraction-sized segments and the reminder will always be included.
Example: "foo-bar"/2.5 will return
({"fo","o-b","ar"}) |
| array(array) | This is similar to dividing a string with an integer, but splits an
array. Example: ({1,2,3,4,5,6,7})/2 will return
({({1,2}),({3,4}),({5,6})}) |
| array(array) | You should be able to predict what this does by now. :) Example:
({1,2,3,4,5,6,7,8})/2.5 will return
({({1,2}),({3,4,5}),({6,7}),({8})}) |
| int | The remainder of a division. If a and b
are integers, a%b is the same as a-(a/b)*b |
| float | The remainder of a division. If a and b
are floats, a%b is the same as a-floor(a/b)*b |
| string | The remainder of a string division. Example: "foo-bar"%2
will return "r" |
| string | The remainder of an array division. Example: ({1,2,3,4,5,6,7})%2
will return ({7}) |
mixed
`+(mixed
arg
)
mixed
`+(object
arg
, mixed
... more
)
int
`+(int
arg
, int
... more
)
float
`+(float
|int
arg
, float
|int
... more
)
string
`+(string
|float
|int
arg
, string
|float
|int
... more
)
array
`+(array
arg
, array
... more
)
mapping
`+(mapping
arg
, mapping
... more
)
multiset
`+(multiset
arg
, multiset
... more
)
Addition/concatenation.
Every expression with the +
operator becomes a call to
this function, i.e. a+b
is the same as
predef::`+(a,b)
. Longer +
expressions are
normally optimized to one call, so e.g. a+b+c
becomes
predef::`+(a,b,c)
.
If there's a single argument, that argument is returned.
If arg
is an object with only one reference and an
lfun::`+=()
, that function is called with the rest of the
arguments, and its result is returned.
Otherwise, if arg
is an object with an lfun::`+()
, that
function is called with the rest of the arguments, and its
result is returned.
Otherwise, if any of the other arguments is an object that has
an lfun::``+()
, the first such function is called with the
arguments leading up to it, and `+()
is then called
recursively with the result and the rest of the arguments.
Otherwise, if arg
is UNDEFINED
and the other arguments are
either arrays, mappings or multisets, the first argument is
ignored and the remaining are added together as described below.
This is useful primarily when appending to mapping values since
m[x] += ({foo})
will work even if m[x]
doesn't
exist yet.
Otherwise the result depends on the argument types:
| The result is the sum of all the arguments. It's a float if any argument is a float. |
| If any argument is a string, all will be converted to strings and concatenated in order to form the result. |
| The array arguments are concatened in order to form the result. |
| The result is like |
| The result is like |
The function is not destructive on the arguments - the result is always a new instance.
In Pike 7.0 and earlier the addition order was unspecified.
The treatment of UNDEFINED
was new
in Pike 7.0.
`-()
, lfun::`+()
, lfun::``+()
mixed
`-(mixed
arg1
)
mixed
`-(mixed
arg1
, mixed
arg2
, mixed
... extras
)
mixed
`-(object
arg1
, mixed
arg2
)
mixed
`-(mixed
arg1
, object
arg2
)
int
`-(int
arg1
, int
arg2
)
float
`-(float
arg1
, int
|float
arg2
)
float
`-(int
|float
arg1
, float
arg2
)
string
`-(string
arg1
, string
arg2
)
array
`-(array
arg1
, array
arg2
)
mapping
`-(mapping
arg1
, array
arg2
)
mapping
`-(mapping
arg1
, mapping
arg2
)
mapping
`-(mapping
arg1
, multiset
arg2
)
multiset
`-(multiset
arg1
, multiset
arg2
)
Negation/subtraction/set difference.
Every expression with the -
operator becomes a call to
this function, i.e. -a
is the same as
predef::`-(a)
and a-b
is the same as
predef::`-(a,b)
. Longer -
expressions are
normally optimized to one call, so e.g. a-b-c
becomes
predef::`-(a,b,c)
.
If there's a single argument, that argument is returned negated.
If arg1
is an object with an lfun::`-()
, that function is
called without arguments, and its result is returned.
If there are more than two arguments the result is:
`-(`-(
.arg1
, arg2
), @extras
)
Otherwise, if arg1
is an object with an lfun::`-()
, that
function is called with arg2
as argument, and its result is
returned.
Otherwise, if arg2
is an object with an lfun::``-()
, that
function is called with arg1
as argument, and its result is
returned.
Otherwise the result depends on the argument types:
arg1
can have any of the following types:
| The result is |
| The result is |
| The result is like |
The function is not destructive on the arguments - the result is always a new instance.
In Pike 7.0 and earlier the subtraction order was unspecified.
If this operator is used with arrays or multisets containing objects
which implement lfun::`==()
but not lfun::`>()
and
lfun::`<()
, the result will be undefined.
`+()
mixed
`*(mixed
arg1
)
mixed
`*(object
arg1
, mixed
arg2
, mixed
... extras
)
mixed
`*(mixed
arg1
, object
arg2
)
array
`*(array
arg1
, int
arg2
)
array
`*(array
arg1
, float
arg2
)
string
`*(string
arg1
, int
arg2
)
string
`*(string
arg1
, float
arg2
)
string
`*(array
(string
) arg1
, string
arg2
)
array
`*(array
(array
) arg1
, array
arg2
)
float
`*(float
arg1
, int
|float
arg2
)
float
`*(int
arg1
, float
arg2
)
int
`*(int
arg1
, int
arg2
)
mixed
`*(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Multiplication/repetition/implosion.
Every expression with the *
operator becomes a call to
this function, i.e. a*b
is the same as
predef::`*(a,b)
. Longer *
expressions are
normally optimized to one call, so e.g. a*b*c
becomes
predef::`*(a,b,c)
.
If there's a single argument, that argument will be returned.
If the first argument is an object that implements lfun::`*()
,
that function will be called with the rest of the arguments.
If there are more than two arguments, the result will be
`*(`*(
.arg1
, arg2
), @extras
)
If arg2
is an object that implements lfun::``*()
, that
function will be called with arg1
as the single argument.
Otherwise the result will be as follows:
arg1
can have any of the following types:
| arg2 can have any of the following types:
| ||||
| The result will be | ||||
| The result will be |
In Pike 7.0 and earlier the multiplication order was unspecified.
`+()
, `-()
, `/()
, lfun::`*()
, lfun::``*()
mixed
`/(object
arg1
, mixed
arg2
)
mixed
`/(mixed
arg1
, object
arg2
)
array
(string
) `/(string
arg1
, int
arg2
)
array
(string
) `/(string
arg1
, float
arg2
)
array
(array
) `/(array
arg1
, int
arg2
)
array
(array
) `/(array
arg1
, float
arg2
)
array
(string
) `/(string
arg1
, string
arg2
)
array
(array
) `/(array
arg1
, array
arg2
)
float
`/(float
arg1
, int
|float
arg2
)
float
`/(int
arg1
, float
arg2
)
int
`/(int
arg1
, int
arg2
)
mixed
`/(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Division/split.
Every expression with the /
operator becomes a call to
this function, i.e. a/b
is the same as
predef::`/(a,b)
.
If there are more than two arguments, the result will be
`/(`/(
.arg1
, arg2
), @extras
)
If arg1
is an object that implements lfun::`/()
, that
function will be called with arg2
as the single argument.
If arg2
is an object that implements lfun::``/()
, that
function will be called with arg1
as the single argument.
Otherwise the result will be as follows:
arg1
can have any of the following types:
| arg2 can have any of the following types:
| ||||
| arg2 can have any of the following types:
| ||||
| The result will be |
Unlike in some languages, the function f(x) = x/n (x and n integers) behaves in a well-defined way and is always rounded down. When you increase x, f(x) will increase with one for each n:th increment. For all x, (x + n) / n = x/n + 1; crossing zero is not special. This also means that / and % are compatible, so that a = b*(a/b) + a%b for all a and b.
`%
mixed
`%(object
arg1
, mixed
arg2
)
mixed
`%(mixed
arg1
, object
arg2
)
string
`%(string
arg1
, int
arg2
)
array
`%(array
arg1
, int
arg2
)
float
`%(float
arg1
, float
|int
arg2
)
float
`%(int
arg1
, float
arg2
)
int
`%(int
arg1
, int
arg2
)
Modulo.
Every expression with the %
operator becomes a call to
this function, i.e. a%b
is the same as
predef::`%(a,b)
.
If arg1
is an object that implements lfun::`%()
then
that function will be called with arg2
as the single argument.
If arg2
is an object that implements lfun::``%()
then
that function will be called with arg2
as the single argument.
Otherwise the result will be as follows:
arg1
can have any of the following types:
| If |
| The result will be
|
For numbers, this means that
a % b
always has the same sign as b
(typically b
is positive;
array size, rsa modulo, etc, and a
varies a
lot more than b
).
The function f(x) = x % n
behaves in a sane way;
as x
increases, f(x)
cycles through the
values 0,1, ..., n-1, 0, ...
. Nothing
strange happens when you cross zero.
The %
operator implements the binary "mod" operation,
as defined by Donald Knuth (see the Art of Computer Programming,
1.2.4). It should be noted that Pike treats %-by-0 as an error
rather than returning 0, though.
/
and %
are compatible, so that
a == b*
for all floor
(a/b) + a%ba
and b
.
`/
, floor()
object
|int
|float
`**(object
|int
|float
arg1
, object
|int
|float
arg2
)
Exponentiation. Raise arg1 to the power of arg2.
The arithmetic operators would be hard to use for anything interesting without the ability to compare the results to each other. For this purpose there are six comparison operators:
Function | Syntax | Identifier | Returns |
Same | a == b | `== | 1 if a is the same value as b, 0 otherwise |
Not same | a != b | `!= | 0 if a is the same value as b, 1 otherwise |
Greater than | a > b | `> | 1 if a is greater than b, 0 otherwise |
Greater than or equal to | a >= b | `>= | 1 if a is greater to or equal to b, 0 otherwise |
Lesser than | a < b | `< | 1 if a is lesser than b, 0 otherwise |
Lesser than or equal to | a <= b | `<= | 1 if a is lesser than or equal to b, 0 otherwise |
The ==
and !=
operators can be used on any type. For two
values to be same they must be the same type. Therefore 1 and 1.0 are
not same. Also, for two values of pointer types to be the same
the two values must be pointers to the same object. It is not enough that
the two objects are of the same size and contain the same data.
The other operators in the table above can only be used with integers, floats and strings. If you compare an integer with a float, the int will be promoted to a float before the comparison. When comparing strings, lexical order is used and the values of the environment variables LC_CTYPE and LC_LANG are respected.
bool
`==(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Equality test.
Every expression with the ==
operator becomes a call to
this function, i.e. a==b
is the same as
predef::`==(a,b)
.
If more than two arguments are given, each argument is compared with the following one as described below, and the test is successful iff all comparisons are successful.
If the first argument is an object with an lfun::`==()
, that
function is called with the second as argument, unless the
second argument is the same as the first argument. The test is
successful iff its result is nonzero (according to `!
).
Otherwise, if the second argument is an object with an
lfun::`==()
, that function is called with the first as
argument, and the test is successful iff its result is nonzero
(according to `!
).
Otherwise, if the arguments are of different types, the test is unsuccessful. Function pointers to programs are automatically converted to program pointers if necessary, though.
Otherwise the test depends on the type of the arguments:
| Successful iff the two integers are numerically equal. |
| Successful iff the two floats are numerically equal and not NaN. |
| Successful iff the two strings are identical, character for character. (Since all strings are kept unique, this is actually a test whether the arguments point to the same string, and it therefore run in constant time.) |
| Successful iff the two arguments point to the same instance. |
Returns 1
if the test is successful, 0
otherwise.
Floats and integers are not automatically converted to test
against each other, so e.g. 0==0.0
is false.
Programs are not automatically converted to types to be compared type-wise.
`!()
, `!=()
bool
`!=(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Inequality test.
Every expression with the !=
operator becomes a call to
this function, i.e. a!=b
is the same as
predef::`!=(a,b)
.
This is the inverse of `==()
; see that function for further
details.
Returns 1
if the test is successful, 0
otherwise.
`==()
bool
`>(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Greater than test.
Every expression with the >
operator becomes a call to
this function, i.e. a>b
is the same as
predef::`>(a,b)
.
Returns 1
if the arguments are strictly decreasing, and
0
(zero) otherwise.
`<()
, `<=()
, `>=()
bool
`>=(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Greater than or equal test.
Every expression with the >=
operator becomes a call to
this function, i.e. a>=b
is the same as
predef::`>=(a,b)
.
Returns 1
if the test is successful, 0
otherwise.
For total orders, e.g. integers, this is the inverse of `<()
.
`<=()
, `>()
, `<()
bool
`<(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Less than test.
Every expression with the <
operator becomes a call to
this function, i.e. a<b
is the same as
predef::`<(a,b)
.
Returns 1
if the test is successful, 0
otherwise.
`<=()
, `>()
, `>=()
bool
`<=(mixed
arg1
, mixed
arg2
, mixed
... extras
)
Less than or equal test.
Every expression with the <=
operator becomes a call to
this function, i.e. a<=b
is the same as
predef::`<=(a,b)
.
Returns 1
if the test is successful, 0
otherwise.
For total orders, e.g. integers, this is the inverse of `>()
.
`<()
, `>()
, `>=()
Logical operators are operators that operate with truth values. In Pike any value except zero is considered true. Logical operators are a very basic part of Pike. They can also decide which arguments to evaluate and which not to evaluate. Because of this most of the logical operators do not have any identifiers and can not be called as normal functions. There are four logical operators:
Function | Syntax | Returns |
And | a && b | If a is false, a is returned and b is not evaluated. Otherwise, b is returned. |
Or | a || b | If a is true, a is returned and b is not evaluated. Otherwise, b is returned. |
Not | ! a | Returns 0 if a is true, 1 otherwise. |
If-else | a ? b : c | If a is true, b is returned and c is not evaluated. Otherwise c is returned and b is not evaluated. |
bool
`!(object
|function
(:void
) arg
)
int(1)
`!(int(0)
arg
)
int(0)
`!(mixed
arg
)
Logical not.
Every expression with the !
operator becomes a call to
this function, i.e. !a
is the same as
predef::`!(a)
.
It's also used when necessary to test truth on objects, i.e. in
a statement if (o) ...
where o
is an object, the
test becomes the equivalent of !!o
so that any
lfun::`!()
the object might have gets called.
If arg
is an object that implements lfun::`!()
, that function
will be called.
If arg
is 0
(zero), a destructed object, or a function in a
destructed object, 1
will be returned.
Otherwise 0
(zero) will be returned.
No float is considered false, not even 0.0
.
`==()
, `!=()
, lfun::`!()
These operators are used to manipulate bits as members in sets. They can also manipulate arrays, multisets and mappings as sets.
Function | Syntax | Identifier | Returns |
Shift left | a << b | `<< | Multiplies a by 2, b times. |
Shift right | a >> b | `>> | Divides a by 2, b times. |
Inverse (not) | ~ a | `~ | Returns -1-a. |
Intersection (and) | a & b | `& | All elements present in both a and b. |
Union (or) | a | b | `| | All elements present in a or b. |
Symmetric difference (xor) | a ^ b | `^ | All elements present in a or b, but not present in both. |
The first three operators can only be used with integers and should be pretty obvious.
The other three, intersection, union and symmetric difference, can be used with integers, arrays, multisets and mappings. When used with integers, these operators considers each bit in the integer a separate element. If you do not know about how bits in integers work I suggest you go look it up in some other programming book or just don't use these operators on integers.
When intersection, union or symmetric difference is used on an array each element
in the array is considered by itself. So intersecting two arrays will result
in an array with all elements that are present in both arrays. Example:
({7,6,4,3,2,1}) & ({1, 23, 5, 4, 7})
will return
({7,4,1})
. The order of the elements in the returned array will
always be taken from the left array. Elements in multisets are treated
the same as elements in arrays. When doing a set operation on a mapping
however, only the indices are considered. The values are just copied with
the indices. If a particular index is present in both the right and left
argument to a set operator, the one from the right side will be used.
Example: ([1:2]) | ([1:3])
will return ([1:3])
.
int
`<<(int
arg1
, int(0..)
arg2
)
mixed
`<<(object
arg1
, int(0..)
|object
arg2
)
mixed
`<<(int
arg1
, object
arg2
)
mixed
`<<(float
arg1
, int(0..)
arg2
)
Left shift.
Every expression with the <<
operator becomes a call to
this function, i.e. a<<b
is the same as
predef::`<<(a,b)
.
If arg1
is an object that implements lfun::`<<()
, that
function will be called with arg2
as the single argument.
If arg2
is an object that implements lfun::``<<()
, that
function will be called with arg1
as the single argument.
If arg1
is a float and arg2
is a non-negative integer,
arg1
will be multiplied by 1<<
.arg2
Otherwise arg1
will be shifted arg2
bits left.
`>>()
int
`>>(int
arg1
, int(0..)
arg2
)
mixed
`>>(object
arg1
, int(0..)
|object
arg2
)
mixed
`>>(int
arg1
, object
arg2
)
float
`>>(float
arg1
, int(0..)
arg2
)
Right shift.
Every expression with the >>
operator becomes a call to
this function, i.e. a>>b
is the same as
predef::`>>(a,b)
.
If arg1
is an object that implements lfun::`>>()
, that
function will be called with arg2
as the single argument.
If arg2
is an object that implements lfun::``>>()
, that
function will be called with arg1
as the single argument.
If arg1
is a float and arg2
is a non-negative integer,
arg1
will be divided by 1<<
.arg2
Otherwise arg1
will be shifted arg2
bits right.
`<<()
mixed
`~(object
arg
)
int
`~(int
arg
)
float
`~(float
arg
)
type
`~(type
|program
arg
)
string
`~(string
arg
)
Complement/inversion.
Every expression with the ~
operator becomes a call to
this function, i.e. ~a
is the same as
predef::`~(a)
.
The result will be as follows:
arg
can have any of the following types:
| If |
| The bitwise inverse of |
| The result will be |
| The type inverse of |
| If |
`!()
, lfun::`~()
mixed
`&(mixed
arg1
)
mixed
`&(mixed
arg1
, mixed
arg2
, mixed
... extras
)
mixed
`&(object
arg1
, mixed
arg2
)
mixed
`&(mixed
arg1
, object
arg2
)
int
`&(int
arg1
, int
arg2
)
string
`&(string
arg1
, string
arg2
)
array
`&(array
arg1
, array
arg2
)
mapping
`&(mapping
arg1
, mapping
arg2
)
mapping
`&(mapping
arg1
, array
arg2
)
mapping
`&(mapping
arg1
, multiset
arg2
)
multiset
`&(multiset
arg1
, multiset
arg2
)
type
`&(type
|program
arg1
, type
|program
arg2
)
Bitwise and/intersection.
Every expression with the &
operator becomes a call to
this function, i.e. a&b
is the same as
predef::`&(a,b)
.
If there's a single argument, that argument is returned.
If there are more than two arguments the result is:
`&(`&(
.arg1
, arg2
), @extras
)
Otherwise, if arg1
is an object with an lfun::`&()
, that
function is called with arg2
as argument, and its result is
returned.
Otherwise, if arg2
is an object with an lfun::``&()
, that
function is called with arg1
as argument, and its result is
returned.
Otherwise the result depends on the argument types:
arg1
can have any of the following types:
| Bitwise and of |
| The result is a string where each character is the bitwise
and of the characters in the same position in |
| The result is like |
| Type intersection of |
The function is not destructive on the arguments - the result is always a new instance.
If this operator is used with arrays or multisets containing objects
which implement lfun::`==()
but not lfun::`>()
and
lfun::`<()
, the result will be undefined.
`|()
, lfun::`&()
, lfun::``&()
mixed
`|(mixed
arg1
)
mixed
`|(mixed
arg1
, mixed
arg2
, mixed
... extras
)
mixed
`|(object
arg1
, mixed
arg2
)
mixed
`|(mixed
arg1
, object
arg2
)
int
`|(int
arg1
, int
arg2
)
string
`|(string
arg1
, string
arg2
)
array
`|(array
arg1
, array
arg2
)
mapping
`|(mapping
arg1
, mapping
arg2
)
multiset
`|(multiset
arg1
, multiset
arg2
)
type
`|(program
|type
arg1
, program
|type
arg2
)
Bitwise or/union.
Every expression with the |
operator becomes a call to
this function, i.e. a|b
is the same as
predef::`|(a,b)
.
If there's a single argument, that argument is returned.
If there are more than two arguments, the result is:
`|(`|(
.arg1
, arg2
), @extras
)
Otherwise, if arg1
is an object with an lfun::`|()
, that
function is called with arg2
as argument, and its result is
returned.
Otherwise, if arg2
is an object with an lfun::``|()
, that
function is called with arg1
as argument, and its result is
returned.
Otherwise the result depends on the argument types:
arg1
can have any of the following types:
| Bitwise or of |
|
|
| The result is a string where each character is the bitwise
or of the characters in the same position in |
| The result is an array with the elements in Every element in |
| The result is like |
| The result is like |
| Type union of |
The function is not destructive on the arguments - the result is always a new instance.
If this operator is used with arrays or multisets containing objects
which implement lfun::`==()
but not lfun::`>()
and
lfun::`<()
, the result will be undefined.
The treatment of UNDEFINED
with multisets was new in Pike 8.1.
`&()
, lfun::`|()
, lfun::``|()
mixed
`^(mixed
arg1
)
mixed
`^(mixed
arg1
, mixed
arg2
, mixed
... extras
)
mixed
`^(object
arg1
, mixed
arg2
)
mixed
`^(mixed
arg1
, object
arg2
)
int
`^(int
arg1
, int
arg2
)
string
`^(string
arg1
, string
arg2
)
array
`^(array
arg1
, array
arg2
)
mapping
`^(mapping
arg1
, mapping
arg2
)
multiset
`^(multiset
arg1
, multiset
arg2
)
type
`^(program
|type
arg1
, program
|type
arg2
)
Exclusive or.
Every expression with the ^
operator becomes a call to
this function, i.e. a^b
is the same as
predef::`^(a,b)
.
If there's a single argument, that argument is returned.
If there are more than two arguments, the result is:
`^(`^(
.arg1
, arg2
), @extras
)
Otherwise, if arg1
is an object with an lfun::`^()
, that
function is called with arg2
as argument, and its result is
returned.
Otherwise, if arg2
is an object with an lfun::``^()
, that
function is called with arg1
as argument, and its result is
returned.
Otherwise the result depends on the argument types:
arg1
can have any of the following types:
| Bitwise exclusive or of |
| The result is a string where each character is the bitwise
exclusive or of the characters in the same position in
|
| The result is an array with the elements in Every element is only matched once against an element in the other array, so if one contains several elements that are equal to each other and are more than their counterparts in the other array, the rightmost remaining elements are kept. |
| The result is like |
| The result is like |
| The result is a type computed like this:
|
The function is not destructive on the arguments - the result is always a new instance.
If this operator is used with arrays or multisets containing objects
which implement lfun::`==()
but not lfun::`>()
and
lfun::`<()
, the result will be undefined.
`&()
, `|()
, lfun::`^()
, lfun::``^()
The index and range operators are used to retrieve information from a complex data type.
Function | Syntax | Identifier | Returns |
Index | a [ b ] | `[] | Returns the index b from a. |
Safe Index | a [? b ] | none | Returns the index b from a, returning 0 instead of an error of a is 0 |
Lookup | a ->identifier | `-> | Looks up the identifier. Same as a["identifier"]. |
Safe Lookup | a ->?identifier | none | Looks up the identifier. Same as a[? "identifier"]. |
Assign index | a [ b ] = c | `[]=; | Sets the index b in a to c. |
Assign index | a ->identifier = c | `->= | Sets the index "identifier" in a to c. |
Range | a [ b .. c ] | `[..] | Returns a slice of a starting at the index b and ending at c. |
Range | a [ .. c ] | `[..] | Returns a slice of a starting at the beginning of a and ending at c. |
Range | a [ b .. ] | `[..] | Returns a slice of a from the index b to the end of a. |
The index operator can be written in two different ways. It can be
written as ob [ index ]
or
ob->identifier
. However, the latter syntax is
equal to ob [ "identifier" ]
.
You can only index strings, arrays, mapping, multisets and objects, and some of these can only be indexed on certain things as shown in this list:
Operation | Returns |
| Returns the ascii value of the Nth character in the string. |
| Return the element in the array corresponding to the integer. |
| Sets the element in the array to the mixed value. |
| Returns the value associated with the index, 0 if it is not found. |
| Associate the second mixed value with the first mixed value. |
| Returns 1 if the index (the value between the brackets) is present in the multiset, 0 otherwise. |
| If the mixed value is true the index is added to the multiset. Otherwise the index is removed from the multiset. |
| Returns the value of the named identifier in the object. |
| Set the given identifier in the object to the mixed value. Only works if the identifier references a variable in the object. |
| Returns the value of the named constant identifier in the program. |
| Returns a piece of the string. |
| Returns a slice of the array. |
When indexing an array
or string
it is sometimes convenient
to access index from the end instead of from the beginning. This function
can be performed by using a negative index. Thus arr[-i]
is the
same as arr[sizeof(arr)-i]
. Note however that this behavior does
not apply to the range operator. Instead the range operator clamps it's
arguments to a suitable range. This means that
a[b..c]
will be treated as follows:
mixed
`[](object
arg
, mixed
index
)
mixed
`[](object
arg
, string
index
)
function
(:void
) `[](int
arg
, string
index
)
int
`[](string
arg
, int
index
)
mixed
`[](array
arg
, int
index
)
mixed
`[](array
arg
, mixed
index
)
mixed
`[](mapping
arg
, mixed
index
)
bool
`[](multiset
arg
, mixed
index
)
mixed
`[](program
arg
, string
index
)
mixed
`[](object
arg
, mixed
start
, mixed
end
)
string
`[](string
arg
, int
start
, int
end
)
array
`[](array
arg
, int
start
, int
end
)
Indexing.
This is the function form of expressions with the []
operator, i.e. a[i]
is the same as
predef::`[](a,i)
.
If arg
is an object that implements lfun::`[]()
, that
function is called with the index
argument.
Otherwise, the action depends on the type of arg
:
arg
can have any of the following types:
| The non-protected (i.e. public) symbol named |
| The bignum function named |
| The character at index |
| If If |
| If |
| If |
| The non-protected (i.e. public) constant symbol |
As a compatibility measure, this function also performs range operations if it's called with three arguments. In that case it becomes equivalent to:
`[..] (arg, start, Pike.INDEX_FROM_BEG, end, Pike.INDEX_FROM_BEG)
See `[..]
for further details.
An indexing expression in an lvalue context, i.e. where the
index is being assigned a new value, uses `[]=
instead of
this function.
`->()
, lfun::`[]()
, `[]=
, `[..]
mixed
`->(object
arg
, string
index
)
mixed
`->(int
arg
, string
index
)
mixed
`->(array
arg
, string
index
)
mixed
`->(mapping
arg
, string
index
)
bool
`->(multiset
arg
, string
index
)
mixed
`->(program
arg
, string
index
)
Arrow indexing.
Every non-lvalue expression with the ->
operator becomes
a call to this function. a->b
is the same as
predef::`^(a,"b")
where "b"
is the symbol
b
in string form.
This function behaves like `[]
, except that the index is
passed literally as a string instead of being evaluated.
If arg
is an object that implements lfun::`->()
, that function
will be called with index
as the single argument.
Otherwise the result will be as follows:
arg
can have any of the following types:
| The non-protected (ie public) symbol named |
| The bignum function named |
| An array of all elements in |
| If |
| If |
| The non-protected (ie public) constant symbol |
In an expression a->b
, the symbol b
can be any
token that matches the identifier syntax - keywords are
disregarded in that context.
An arrow indexing expression in an lvalue context, i.e. where
the index is being assigned a new value, uses `->=
instead of
this function.
`[]()
, lfun::`->()
, ::`->()
, `->=
mixed
`[]=(object
arg
, mixed
index
, mixed
val
)
mixed
`[]=(object
arg
, string
index
, mixed
val
)
mixed
`[]=(array
arg
, int
index
, mixed
val
)
mixed
`[]=(mapping
arg
, mixed
index
, mixed
val
)
bool
`[]=(multiset
arg
, mixed
index
, bool
val
)
Index assignment.
Every lvalue expression with the []
operator becomes a
call to this function, i.e. a[b]=c
is the same as
predef::`[]=(a,b,c)
.
If arg
is an object that implements lfun::`[]=()
, that function
will be called with index
and val
as the arguments.
arg
can have any of the following types:
| The non-protected (ie public) variable named |
| Index |
| If |
val
will be returned.
An indexing expression in a non-lvalue context, i.e. where the
index is being queried instead of assigned, uses `[]
instead
of this function.
`->=()
, lfun::`[]=()
, `[]
mixed
`->=(object
arg
, string
index
, mixed
val
)
mixed
`->=(mapping
arg
, string
index
, mixed
val
)
bool
`->=(multiset
arg
, string
index
, bool
val
)
Arrow index assignment.
Every lvalue expression with the ->
operator becomes a
call to this function, i.e. a->b=c
is the same as
predef::`->=(a,"b",c)
where "b"
is the symbol
b
in string form.
This function behaves like `[]=
, except that the index is
passed literally as a string instead of being evaluated.
If arg
is an object that implements lfun::`->=()
, that function
will be called with index
and val
as the arguments.
arg
can have any of the following types:
| The non-protected (ie public) variable named |
| Index |
| If |
val
will be returned.
In an expression a->b=c
, the symbol b
can be any
token that matches the identifier syntax - keywords are
disregarded in that context.
An arrow indexing expression in a non-lvalue context, i.e. where
the index is being queried instead of assigned, uses `->
instead of this function.
`[]=()
, lfun::`->=()
, `->
mixed
`[..](object
arg
, mixed
start
, int
start_type
, mixed
end
, int
end_type
)
string
`[..](string
arg
, int
start
, int
start_type
, int
end
, int
end_type
)
array
`[..](array
arg
, int
start
, int
start_type
, int
end
, int
end_type
)
Extracts a subrange.
This is the function form of expressions with the [..]
operator. arg
is the thing from which the subrange is to be
extracted. start
is the lower bound of the subrange and
end
the upper bound.
start_type
and end_type
specifies how the start
and
end
indices, respectively, are to be interpreted. The types
are either Pike.INDEX_FROM_BEG
, Pike.INDEX_FROM_END
or
Pike.OPEN_BOUND
. In the last case, the index value is
insignificant.
The relation between [..]
expressions and this function
is therefore as follows:
a[i..j] <=> `[..] (a, i, Pike.INDEX_FROM_BEG, j, Pike.INDEX_FROM_BEG)
a[i..<j] <=> `[..] (a, i, Pike.INDEX_FROM_BEG, j, Pike.INDEX_FROM_END)
a[i..] <=> `[..] (a, i, Pike.INDEX_FROM_BEG, 0, Pike.OPEN_BOUND)
a[<i..j] <=> `[..] (a, i, Pike.INDEX_FROM_END, j, Pike.INDEX_FROM_BEG)
a[<i..<j] <=> `[..] (a, i, Pike.INDEX_FROM_END, j, Pike.INDEX_FROM_END)
a[<i..] <=> `[..] (a, i, Pike.INDEX_FROM_END, 0, Pike.OPEN_BOUND)
a[..j] <=> `[..] (a, 0, Pike.OPEN_BOUND, j, Pike.INDEX_FROM_BEG)
a[..<j] <=> `[..] (a, 0, Pike.OPEN_BOUND, j, Pike.INDEX_FROM_END)
a[..] <=> `[..] (a, 0, Pike.OPEN_BOUND, 0, Pike.OPEN_BOUND)
The subrange is specified as follows by the two bounds:
If the lower bound refers to an index before the lowest allowable (typically zero) then it's taken as an open bound which starts at the first index (without any error).
Correspondingly, if the upper bound refers to an index past the last allowable then it's taken as an open bound which ends at the last index (without any error).
If the lower bound is less than or equal to the upper bound, then the subrange is the inclusive range between them, i.e. from and including the element at the lower bound and up to and including the element at the upper bound.
If the lower bound is greater than the upper bound then the result is an empty subrange (without any error).
The returned value depends on the type of arg
:
arg
can have any of the following types:
| A string with the characters in the range is returned. |
| An array with the elements in the range is returned. |
| If the object implements As a compatibility measure, if the object does not implement
Note that |
lfun::`[..]
, `[]
When indexing data-structures, it is common for some elements
to be optional and thus evaluate to 0
. To simplify
deep indexing of such stuctures, the conditional (aka "safe")
indexing operators exist (->?
, [?]
and (?)
). They behave like the corresponding "unsafe"
operator (ie ->
, []
and ()
),
except for short-circuiting the remainder of the expression to
0
when the indexed value is 0
.
Examples:
mapping m = ([
"a":([
"x": ([]),
]),
"b":0,
]);
Expression | Result |
m->a->x->i | UNDEFINED |
m->a->y->i | Indexing the NULL value with "i". |
m->a->y->?i | UNDEFINED (m->a->y ) |
m->b->x->i | Indexing the NULL value with "x". |
m->b->?x->i | 0 (m->b ) |
m->a->?y->i | Indexing the NULL value with "i". |
m->a->?y->?i | UNDEFINED (m->a->y ) |
There is really only one assignment operator, but it can be combined with lots of other operators to make the code shorter. An assignment looks like this:
variable = expression;
The variable can be a local variable, a global variable or an index
in an array, object, multiset or mapping. This will of course set the
value stored in variable to expression. Note that the above
is also an expression which returns the value of the expression.
This can be used in some interesting ways:
variable1 = variable2 = 1; // Assign 1 to both variables
variable1 =(variable2 = 1); // Same as above
// Write the value of the expression, if any
if(variable = expression)
write(variable);
Using assignments like this can however be confusing to novice users, or users
who come from a Pascal or Basic background. Especially the if statement
can be mistaken for if(variable == expression) which
would mean something completely different. As I mentioned earlier, the
assignment operator can be combined with another operator to form operators
that modify the contents of a variable instead of just assigning it.
Here is a list of all the combinations:Syntax | Same as | Function |
variable += expression | variable = variable + expression | Add expression to variable |
variable -= expression | variable = variable - expression | Subtract expression from variable |
variable *= expression | variable = variable * expression | Multiply variable with expression |
variable /= expression | variable = variable / expression | Divide variable by expression |
variable %= expression | variable = variable % expression | Modulo variable by expression |
variable <<= expression | variable = variable << expression | Shift variable expression bits left |
variable >>= expression | variable = variable >> expression | Shift variable expression bits right |
variable |= expression | variable = variable | expression | Or variable with expression |
variable &= expression | variable = variable & expression | And variable with expression |
variable ^= expression | variable = variable ^ expression | Xor variable with expression |
In all of the above expressions variable can actually be any of type of assignable values. Assignable values are also known as lvalues and here is a list of lvalues:
Lvalue type | Syntax | Valid assignment type |
a local or global variable | identifier | same as variable |
an element in an array | array [ int ] | any type |
elements in elements in an array | array [ string ] | any type This is like map(arr, `[]=,string_indexing_element, assignment_element) |
an element in an string | string [ int ] | integer |
an element in a mapping | mapping[mixed] or mapping->identifier | any type |
an element in a multiset | multiset[mixed] or multiset->identifier | true / false |
a variable in an object | object[string] or object->identifier | same type as named variable |
a list of lvalues | [ lvalue, lvalue ] | an array, first value in the array will be assigned to the first lvalue in the list, second value in the array to the second value in the list etc. |
Now there are only a couple of operators left. I have grouped them together in this section, not because they are not important, but because they do not fit in any particular categories.
Function | Syntax | Identifier | Returns |
Calling | a ( args ) | `() | Calls the function a. |
Safe Calling | a (? args ) | `() | Calls a, unless a is 0. |
splice | @ a | none | Sends each element in the array a as an individual argument to a function call. |
Increment | ++ a | none | Increments a and returns the new value of a. |
Decrement | -- a | none | Decrements a and returns the new value of a. |
Post increment | a ++ | none | Increments a and returns the old value of a. |
Post decrement | a -- | none | Decrements a and returns the old value of a. |
casting | (type) a | none | Tries to convert a into a value of the specified type. |
Null | a, b | none | Evaluates a and b, then returns b. |
The most important of these operators is the calling operator. It is used to call functions. The operator itself is just a set of parenthesis placed after the expression that returns the function. Any arguments to the function should be placed between the parenthesis, separated by commas. We have already seen many examples of this operator, although you might not have realized it was an operator at the time. The function call operator can do more than just calling functions though; if the 'function' is in fact an array, the operator will loop over the array and call each element in the array and returns an array with the results.
If on the other hand, the 'function' is a program, the operator will clone an object from the program and call create() in the new object with the arguments given. In fact, the function clone is implemented like this:
object clone(mixed p, mixed ... args) { ( (program)p )(@args); }
Placing a '?' character immediately after the open parentehsis
makes the operator return 0 instead of an error if the function
expression evalutates to 0.
On the subject of function calls, the splice operator should also be mentioned.
The splice operator is an at sign in front of an expression. The expression
should always be an array. The splice operator sends each of the elements
in the array as a separate argument to the function call. The splice operator
can only be used in an argument list for a function call.Then there are the increment and decrement operators. The increment and decrement operators are somewhat limited: they can only be used on integers. They provide a short and fast way to add or subtract one to an integer. If the operator is written before the variable (++a) the returned value will be what the variable is after the operator has added/subtracted one to it. If the operator is after the variable (a++) it will instead return the value of the variable before it was incremented/decremented.
Casting is used to convert one type to another, not all casts are possible. Here is a list of all casts that actually _do_ anything:
casting from | to | operation |
int | string | Convert the int to ASCII representation |
float | string | Convert the float to ASCII representation |
string | int | Convert decimal, octal or hexadecimal number to an int. Note that this will only work with decimal numbers in future versions. |
string | float | Convert ASCII number to a float. |
string | program | String is a filename, compile the file and return the program. Results are cached. |
string | object | This first casts the string to a program, (see above) and then clones the result. Results are cached. |
object | type | This calls the function 'cast' with a string containing the type as an argument. |
string | array | Same as doing values(string) |
array(int) | string | This does the inverse of the operation above. Ie. it constructs a string from an array of integers. |
array | array(type) | This recursively casts all values in the array to type. |
mapping | array | Same as Array.transpose(({indices(mapping),values(mapping)) . Example: (array)([1:2,3:4]) will return ({ ({1,2}), ({3,4}) }) |
multiset | array | Same as doing indices(multiset) . |
int | float | Returns a float with the same value as the integer. |
float | int | Returns the integer closest to the float. |
function | object | Same as function_object(function) . |
You can also use the cast operator to tell the compiler things. If a is a variable of type mixed containing an int, then the expression (int)a can be used instead of a and that will tell the compiler that the type of that expression is int.
Last, and in some respect least, is the comma operator. It doesn't do much. In fact, it simply evaluates the two arguments and then returns the right hand one. This operator is mostly useful to produce smaller code, or to make defines that can be used in expressions.
mixed
`()(function
(:void
) fun
, mixed
... args
)
mixed
call_function(function
(:void
) fun
, mixed
... args
)
Call a function.
Calls the function fun
with the arguments specified by args
.
lfun::`()()
When evaluating an expression, you can always use parenthesis to tell the compiler in which order to evaluate things. Normally, the compiler will evaluate things from left to right, but it will evaluate operators with higher priority before those with lower. The following table shows the relative priority of all the operators in descending order:
(a) a() a[b] a->b a[b..c] ({}) ([]) (<>) |
!a ~a (type)a ++a --a |
a++ a-- |
a*b a/b a%b |
a+b a-b |
a>>b a<<b |
a>b a>=b a<b a<=b |
a==b a!=b |
a&b |
a^b |
a|b |
&& |
|| |
a?b:c |
= |
@a |
, |
Examples:
The expression | is evaluated in this order: |
1+2*2 | 1+(2*2) |
1+2*2*4 | 1+((2*2)*4) |
(1+2)*2*4 | ((1+2)*2)*4 |
1+4,c=2|3+5 | (1+4),(c=((2|3)+5)) |
1+5 & 4 == 3 | (1+(5 & 4)) == 3 |
c=1,99 | (c=1),99 |
!a++ + ~--a() | (!(a++)) + (~((--a)())) |
As mentioned earlier a + b can just as well be written as `+(a, b). Together with the function map which calls a function for every index in an array and the splice operator this can be used to create some very very fast and compact code. Let's look at some examples: