4. Operators

  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.

4.1. Arithmetic operators

  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

  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 + int

  int

  the sum of the two values

      float + int
      int + float
      float + float

  float

  the sum of the two values

      string + string
      int + string
      float + string
      string + int
      string + float

  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 + array

  array

  The two arrays are concatenated into a new array and that new array is returned.

      mapping + mapping

  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 + multiset

  multiset

  A multiset with all the indices from both multisets is returned.

      int - int

  int

  The right value subtracted from the left.

      float - int
      int - float
      float - float

  float

  The right value subtracted from the left.

      string - string

  string

  A copy of the left string with all occurrences of the right string removed.

      array - array

  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 - mapping

  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 - multiset

  multiset

  A copy of the left multiset without any index present in the left multiset.

      - int

  int

  Same as `0 - int`.

      - float

  float

  Same as `0 - float`.

      int * int

  int

  the product of the two values

      float * int
      int * float
      float * float

  float

  the product of the two values

      array(string) * string

  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(array) * array

  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 * int

  string

  This operation will concatenate the string N times. Example: `"foo"*3` will return `"foofoofoo"`.

      string * float

  string

  This operation will concatenate the string X times. Example: `"foo"*2.5` will return `"foofoofo"`.

      array * int

  string

  This operation will concatenate the array N times. Example: `({"foo"})*3` will return `({"foo","foo","foo"})`.

      array * float

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

  int

  The right integer divided by the left integer rounded towards minus infinity.

      float / int
      int / float
      float / float

  float

  The right value divided by the left value.

      string / string

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

      string / int

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

      string / float

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

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

  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 % int

  int

  The remainder of a division. If `a` and `b` are integers, `a%b` is the same as `a-(a/b)*b`

      float % float
      int % float
      float % int

  float

  The remainder of a division. If `a` and `b` are floats, `a%b` is the same as `a-floor(a/b)*b`

      string % int

  string

  The remainder of a string division. Example: `"foo-bar"%2` will return `"r"`

      array % int

  string

  The remainder of an array division. Example: `({1,2,3,4,5,6,7})%2` will return `({7})`

4.2. Comparison operators

  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.

4.3. Logical operators

  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 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.

4.4. Bitwise/set operators

  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])`.

4.5. Indexing

  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.
  Lookup | `a ->identifier` | ``->` | 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

      string[int]

  Returns the ascii value of the Nth character in the string.

      array[int]

  Return the element in the array corresponding to the integer.

      array[int]=mixed

  Sets the element in the array to the mixed value.

      mapping[mixed]
      mapping->identifier

  Returns the value associated with the index, 0 if it is not found.

      mapping[mixed]=mixed
      mapping->identifier=mixed

  Associate the second mixed value with the first mixed value.

      multiset[mixed]
      multiset->identifier

  Returns 1 if the index (the value between the brackets) is present in the multiset, 0 otherwise.

      multiset[mixed]=mixed
      multiset->identifier=mixed

  If the mixed value is true the index is added to the multiset. Otherwise the index is removed from the multiset.

      object[string]
      object->identifier

  Returns the value of the named identifier in the object.

      object[string]=mixed
      object->identifier=mixed

  Set the given identifier in the object to the mixed value. Only works if the identifier references a variable in the object.

      program[string]
      program->identifier

  Returns the value of the named constant identifier in the program.

      string[int..int]

  Returns a piece of the string.

      array[int..int]

  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:

  - If b is less than zero, the range will begin at the start of the array as if b had been zero.
  - If b is greater or equal to sizeof(a) an empty array/string will be returned.
  - If c is less than b, an empty array/string will be returned.
  - If c is greater or equal to sizeof(a) the range will continue to the end of the array/string.
  - No errors are generated in any of the above cases.

4.6. The assignment operators

  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.

4.7. The rest of the operators

  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.
  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); }

  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.

4.8. Operator precedence

  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)()))`

4.9. Operator functions

  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:

  `map(arr, `-)`
    This will return an array with each element negated.
  `map(text/"\n",`/," ")`
    This will divide a text into lines, each line will then be mapped through ``/` and divided into an array of words.
  ``+(0, @arr)`
    This will add all the integers in the array `arr` together.
  `int abs(int a) { return ( a>0 ? `+ : `-)(a); }`
    This is a rather absurd but working function which will return the absolute value of a.