Subscripting and Function Side-effects

In an assignment statement, with left-hand-side subscripts:

lhs_var[lhs_expr1][lhs_expr2]... = rhs_expr

The expressions are evaluated, and any subscripting is performed, from left to right. It is possible to have function calls in the right-hand-side expression, or in any of the left-hand-side expressions. If a function call has the side-effect of modifying the lhs_var, it is not defined whether those changes will appear in the final value of the lhs_var, once the assignment has been completed. To be sure about what is going to happen, perform the function call in a separate statement, i.e. do not try to modify the lhs_var in two different ways in the same statement. Where there are no left-hand-side subscripts, you can always assume that the final value of the lhs_var will be the value of rhs_expr, regardless of any side-effects that may have changed lhs_var.

Slicing Sequences

A sequence of consecutive elements may be selected by giving the starting and ending element numbers. For example if x is {1, 1, 2, 2, 2, 1, 1, 1} then x[3..5] is the sequence {2, 2, 2}. x[3..3] is the sequence {2}. x[3..2] is also allowed. It evaluates to the zero length sequence {}. If y has the value: {"fred", "george", "mary"} then y[1..2] is {"fred", "george"}.

We can also use slices for overwriting portions of variables. After x[3..5] = {9, 9, 9} x would be {1, 1, 9, 9, 9, 1, 1, 1}. We could also have said x[3..5] = 9 with the same effect. Suppose y is {0, "Euphoria", 1, 1}. Then y[1..4] is "Euph". If we say y[1..4] = "ABCD" then y will become {0, "ABCDoria", 1, 1}.

In general, a variable name can be followed by 0 or more subscripts, followed in turn by 0 or 1 slices. Only variables may be subscripted or sliced, not expressions.

We need to be a bit more precise in defining the rules for empty slices. Consider a slice s[i..j] where s is of length n. A slice from i to j, where j = i - 1 and i >= 1 produces the empty sequence, even if i = n + 1. Thus 1..0 and n + 1..n and everything in between are legal (empty) slices. Empty slices are quite useful in many algorithms. A slice from i to j where j < i - 1 is illegal , i.e. "reverse" slices such as s[5..3] are not allowed.

We can also use the \$ shorthand with slices, e.g.

s[2..\$]
s[5..\$-2]
s[\$-5..\$]
s[\$][1..floor(\$/2)] -- first half of the last element of s

Concatenation of Sequences and Atoms - The '&' Operator

Any two objects may be concatenated using the & operator. The result is a sequence with a length equal to the sum of the lengths of the concatenated objects:

{1, 2, 3} & 4              -- {1, 2, 3, 4}

4 & 5                      -- {4, 5}

{{1, 1}, 2, 3} & {4, 5}    -- {{1, 1}, 2, 3, 4, 5}

x = {}
y = {1, 2}
y = y & x                  -- y is still {1, 2}

You can delete element i of any sequence s by concatenating the parts of the sequence before and after i:

s = s[1..i-1] & s[i+1..length(s)]

This works even when i is 1 or length(s), since s[1..0] is a legal empty slice, and so is s[length(s)+1..length(s)].

Sequence Formation

Finally, sequence formation, using braces and commas:

{a, b, c, ... }

is also an operator. It takes n operands, where n is 0 or more, and makes an n-element sequence from their values:

integer apple = 4
sequence orange = {5,6}
atom foobar = 0.0001

x = {apple, orange*2, {1,2,3}, 99/4+foobar} -- sequence formation

? x
-- {4, {10,12}, {1,2,3}, 24.7501 }

The sequence-formation operator is listed at the bottom of the a precedence chart.

Functions

Some other important operations that you can perform on sequences have English names, rather than special characters. These operations are built into the Interpreter (eui.exe and euiw.exe), so they'll always be there, and so they'll be fast. They are described in detail in the Language Reference, but are important enough to Euphoria programming that we should mention them here. You call these routines as if they were functions, although they are actually implemented much more efficiently than that.

length(sequence s1 )

Returns the length of a sequence s1.

This is the number of elements in s1. Some of these elements may be sequences that contain elements of their own, but length just gives you the "top-level" count. Note however that the length of an atom is always 1.

length({5,6,7})             -- 3
length({1, {5,5,5}, 2, 3})  -- 4 (not 6!)
length({})                  -- 0
length(5)                   -- 1
repeat(object o1, integer times)

Returns a sequence that consists of an item o1 repeated count times:

repeat(0, 100)         -- {0,0,0,...,0}   i.e. 100 zeros
repeat("Hello", 3)     -- {"Hello", "Hello", "Hello"}
repeat(99,0)           -- {}

The item to be repeated can be any atom or sequence.

append(sequence s1, object o1)

Returns a sequence by adding an object o1 to the end of a sequence s1.

append({1,2,3}, 4)         -- {1,2,3,4}
append({1,2,3}, {5,5,5})   -- {1,2,3,{5,5,5}}
append({}, 9)              -- {9}

The length of the new sequence is always one greater than the length of the original sequence. The item to be added to the sequence can be any atom or sequence.

prepend(sequence s1, object o1)

Returns a new sequence by adding an element to the beginning of a sequence s:.

append({1,2,3}, 4)         -- {1,2,3,4}
prepend({1,2,3}, 4)        -- {4,1,2,3}

append({1,2,3}, {5,5,5})   -- {1,2,3,{5,5,5}}

append({}, 9)              -- {9}
prepend({}, 9)             -- {9}

The length of the new sequence is always one greater than the length of the original sequence. The item to be added to the sequence can be any atom or sequence.

These two built-in functions, append() and prepend(), both increase the length of a sequence, as does the concatenation operator &, but there are important differences:

-- appending a sequence is different
append({1,2,3}, {5,5,5})   -- {1,2,3,{5,5,5}}
{1,2,3} & {5,5,5}          -- {1,2,3,5,5,5}

-- appending an atom is the same
append({1,2,3}, 5)         -- {1,2,3,5}
{1,2,3} & 5                -- {1,2,3,5}
insert(sequence in_what, object what, atom position)

This function takes a target sequence, in_what, shifts its tail one notch and plugs the object what in the hole just created. The modified sequence is returned:

s = insert("Joe",'h',3)     -- s is "Johe", another string
s = insert("Joe","h",3)     -- s is {'J','o',{'h'},'e'}, not a string
s = insert({1,2,3},4,-0.5)  -- s is {4,1,2,3}, like prepend()
s = insert({1,2,3},4,8.5)   -- s is {1,2,3,4}, like append()

The length of the returned sequence is one more than the one of in_what. This is the same rule as for append() and prepend() above, which are actually special cases of insert().

splice(sequence in_what, object what, atom position)

If what is an atom, this is the same as insert(). But if what is a sequence, that sequence is inserted as successive elements into in_what at position:

s = splice("Joe",'h',3)
-- s is "Johe", like insert()
s = splice("Joe","hn Do",3)
-- s is "John Doe", another string
s = splice("Joh","n Doe",9.3)
-- s is "John Doe", like with the & operator
s = splice({1,2,3},4,-2)
-- s is {4,1,2,3}, like with the & operator in reversed order

The length of splice() always is length(in_what) plus length(what), like for concatenation using &.