8 Think Euphoria

8.1 Sequence: List

Eu) back next


Just like a string, a list is represented by a sequence. In a string the sequence is composed of characters; in a list, they can be any type. The values in a list are called elements and are indexed like any other sequence. Euphoria is naturally generic; strings and lists behave the same way.



8.2 A list is a sequence



A sequence can be used to represent a list. A list is an "ordered set" were each value is identified by an index . The values that make up a list are called its elements . The list is a common generic data structure found in computer science. The Euphoria sequence is well suited to represent any list you could imagine. The sequence data-type allows any object to be used for its elements. Notice that sequences used for strings and for lists behave the same way--you only have one thing to learn.



8.3 Sequence values



There are several ways to create a new sequence

 
    {10, 20, 30, 40} 
    { "crunch frog", "ram bladder", "lark vomit" } 

The first example is a sequence of four integers. The second is a sequence of three strings. The elements of a sequence don't have to be the same type. The following sequence contains a string, a atom, an integer, and (lo!) another sequence:

 
    { "spam", 2.0, 5, { 10, 20 } } 

A sequence within another sequence is said to be nested . A sequence that contains no elements is called an empty sequence , you can create one with empty brackets {} ( or for strings "". )

As you might expect, you can assign sequences to variables:

 
    sequence cheeses = { "Cheddar", "Edam", "Gouda" } 
    sequence numbers = { 17, 123 } 
    sequence empty = {} 
 
    include std.e 
    pretty_print(1, cheeses, {2} ) 
        -- { "Cheddar", "Edam", "Gouda" } 
    ? numbers 
        -- { 17, 123 } 
    ? empty 
        -- {} 



8.4 Sequences are mutable



The syntax for accessing the elements of a sequence is the same as the syntax for accessing the characters of a string--the bracket operator ( [ ] ). The expression inside the brackets specifies the index. Remember that the indices start at 1:

 
    sequence numbers = { 4, 2.5, 0, 10 } 
    ? numbers[2]  
        -- 2.5 

 
    puts(1, cheeses[1] ) 
        -- Cheddar 

The bracket operator can appear anywhere in an expression. When it appears on the left side of an assignment, it identifies element(s) in the sequence that will be assigned.

 
    sequence numbers = { 17, 123 } 
    numbers[2] = 5 
    ? numbers 
        -- { 17, 5 } 

So the two-eth element of numbers, which used to be 123, is now 5.

You can think of a sequence as a relationship between indices and elements. Mathematicians call this relationship is called a mapping , each index "maps to" one of the elements. Here is a state diagram showing cheeses, numbers, and empty:

png/08_state.png

Sequences are represented by boxes with the word "sequence" outside and the elements of the list inside. cheeses refers to a sequence with three elements indexed 1, 2, and 3. numbers contains two elements; the diagram shows that the value of the second element has been reassigned from 123 to 5. empty refers to a sequence with no elements.

Sequence indices work the same way as string indices (after all strings are sequences):

You can search for elements within a sequence:

 
    sequence cheeses = { "Cheddar", "Edam", "Gouda" } 
    ? find( "Edam", cheeses ) 
        -- 2  
        -- i.e. true 
    ? find( "Brie", cheeses ) 
        -- 0 
        -- i.e. false 

You can do calculations within the ( [ ] ) brackets.

 
    sequence numbers 
    numbers = {4,2.5,0, 10} 
    ? numbers[5-3] 
        -- 2.5 

If your index value is not an integer, then it will be truncated to and integer value and then applied:

 
    sequence numbers 
    numbers = {4,2.5,0, 10} 
    ? numbers[1.7] 
        -- 4 

If you try to read or write an element that does not exist, you get a runtime error:

 
    sequence numbers 
    numbers = {4,2.5,0, 10} 
    ? numbers[10] 
        -- subscript value 10 is out of bounds, 
        -- reading from a sequence of length 4 

numbers[$] is the last element of the list, numbers[$-1] is the second to last, and numbers[$-5] doesn't exist.

 
    sequence numbers = {4,2.5,0, 10} 
    ? numbers[$] 
    ? numbers[$-1] 
        -- 10 
        -- 0 

Sequences operate the say way all the time, regardless if you call them strings or lists.



8.5 Traversing a sequence



It is common to use a loop variable as a sequence index.

 
    sequence horsemen = {"war", "famine", "pestilence", "death" } 
    integer i = 1 
    while i <= 4 do 
        puts(1, horsemen[i] ) 
        puts(1, "\n" ) 
        i = i + 1 
    end while 
        -- war 
        -- famine 
        -- pestilence 
        -- death 

This while loop counts from 1 to 4. When the loop variable i is 5, the condition fails and the loop terminates. So the code block of the loop is only executed when i is 1, 2, 3, and 4.

Each time through the loop, the variable i is used as an index into the list, printing the i-eth element. This pattern of computation is called a sequence traversal . A shorter way to do the same thing is to use a for loop:

 
    for i=1 to length(horsemen) do 
        puts(1, horsemen[i] & "\n" ) 
    end for 
        -- war 
        -- famine 
        -- pestilence 
        -- death 

You may use a loop to write or update elements of a sequence.

 
    for i = 1 to length( numbers ) do 
        numbers[i] = numbers[i] * 2 
    end for 

A for loop over an empty sequence never executes the code block:

 
    sequence empty = {} 
    for i = 1 to length(empty) do 
        puts(1, "This never happens" ) 
    end for 

Although a sequence can contain another sequence, the nested sequence still counts as a single element. The length of this sequence is four:

 
    { "spam", 1, { "Brie", "Roquefort", "Pol le Veq"}, {1,2,3} } 

8.6 toms ?? an in operator



8.7 Series



Sequences that contain consecutive integers are common, so there is a simple way to create them:

 
    include std/seqence.e 
 
    ? linear( 1, 1, 5 ) 
        -- { 1, 2, 3, 4, 5 } 

The first argument could be a starting sequence:

 
    ? linear( {1,2,3}, 4 ) 
        --{ 
        --  {1,2,3}, 
        --  {5,6,7}, 
        --  {9,10,11} 
        --} 

You may even use characters as an argument:

 
    pretty_print(1, linear( 'a', 1, 26 ) ) 
        -- {97'a',98'b',99'c',100'd',101'e',102'f',103'g',104'h',105'i',106'j', 
        -- 107'k', 108'l',109'm',110'n',111'o',112'p',113'q',114'r',115's', 
        -- 116't',117'u',118'v', 119'w',120'x',121'y',122'z'} 

When there is a third argument, it specifies the space between successive values, which is called the step size .

Notice, there is a special sequence that contains no elements. It is called the empty sequence , and it is denoted ( { } ).

With all these ways to create sequences, it would be disappointing if we couldn't assign sequence values to variables or pass sequences as arguments to routines. We can.

 
    include std.e 
 
    sequence vocabulary, numbers, empty 
    vocabulary = {"ameliorate","castigate","defenestrate"} 
    numbers = {17,123} 
    empty = {} 
    pretty_print(1, {vocabulary, numbers, empty}, {2} ) 
 
        -- {{"ameliorate","castigate","defenestrate"},{17,123},{}} 



8.8 Sequence length



The function length() returns the length of a sequence. It is a good idea to use this value as the upper bound of a loop instead of a constant. That way, if the size of the sequence changes, you won't have to go through the program changing all the loops; they will work correctly for any size of sequence:

 
    sequence horsemen = {"war", "famine", "pestilence", "death" } 
        integer i = 1 
    while i <= length(horsemen) do 
        puts(1, horsemen[i] ) 
        puts(1, "\n" ) 
        i = i + 1 
    end while 
        -- war 
        -- famine 
        -- pestilence 
        -- death 

The last time the code block of the loop is executed, i is length(horsemen), which is the index of the last element. When i is equal to length(horsemen)+1, the condition fails and the code block is not executed, which is a good thing, because length(horsemen) is the last legal index.

Although a sequence can contain another sequence, the nested sequence still counts as a single element. The length of this sequence is four:

 
    { "spam!", 1, {"Brie", "Roquefort", "Pol le Veq"}, { 1, 2, 3 } } 



8.9 Sequence membership



in is a boolean operator that tests membership in a sequence. We used it with strings, but it also works with lists and other sequences:

 
    sequence horsemen = {"war","famine","pestilence","death"} 
    ? find( "pestilence", horsemen) 
    ? find( "debauchery", horsemen) 
        -- 3 
        -- 0 

Since "pestilence" is a member of the horsemen sequence, the in find() function returns 3. Since "debauchery" is not in the sequence, in returns 0 (for false). We can use the not in combination with find() to test whether an element is not a member of a sequence:

 
    sequence horsemen = {"war","famine","pestilence","death"} 
    ? not find( "pestilence", horsemen) 
    ? not find( "debauchery", horsemen) 
        -- 0 
        -- 1 

In this example it is false that "pestilence" in not in the horsemen sequence. While it is true that "debaucher" is not in the horsemen sequence.



8.10 Sequences and for loops



The for loop we saw in Section 7.3 also works with sequences. The generalized syntax of a for loop is:

 
    for index = start to finish do 
        -- code block 
            end for 

This statement is equivalent to:

 
    integer index = start 
    while index < length(list) do 
        -- code block 
            end while 

The for loop is more concise because we can eliminate the declaration of the loop variable, index. Here is the previous loop written with a for loop.

 
    sequence horsemen = {"war", "famine", "pestilence", "death" } 
    for i=1 to length(horsemen) do 
        puts(1, horsemen[i] ) 
        puts(1, "\n" ) 
    end for 

It almost reads like English: "For (every) horseman in (the list of) horsemen, print (the name of the) horseman."

Any sequence expression can be used in a for loop:

 
    function range( sequence args ) 
        sequence list = {} 
        if length( args ) = 1 then 
            for i=1 to args[1] do 
                list = append( list, i ) 
            end for 
        elsif length( args ) = 2 then 
            for i = args[1] to args[2] do 
                list = append( list, i ) 
            end for 
        elsif length(args ) = 3 then 
            for i = args[1] to args[2] by args[3] do 
                list = append( list, i ) 
            end for 
        else 
            list = {} 
        end if 
        return list 
    end function 
 
    sequence stuff = range( {12} ) 
 
    for number = 1 to length(stuff) do 
        if floor(stuff[number]/2) then 
            ? stuff[number] 
        end if 
    end for 
        -- 2 
        -- 4 
        -- 6 
        -- 8 
        -- 10 
        -- 12 

 
    sequence tropical = {"banana","apple","quince"} 
    for fruit=1 to length(tropical) do 
        printf(1, "I like to eat %ss.\n", {tropical[fruit]} ) 
    end for 
        -- I like to eat bananas. 
        -- I like to eat apples. 
        -- I like to eat quinces. 

The first example prints all the even numbers between zero and nineteen. The second example expresses enthusiasm for various fruits.



8.11 Sequence operations



The ( & ) operator concatenates sequences:

 
    sequence a, b, c 
    a = {1,2,3} 
    b = {4,5,6} 
    c = a & b 
    ? c 
        -- {1,2,3,4,5,6} 

The repeat() function repeats elements a number of times to create a sequence:

 
    ? repeat( 0, 4 ) 
        -- {0,0,0,0} 
 
    ? repeat( {1,2,3}, 3 ) 
        -- { 
        --   {1,2,3}, 
        --   {1,2,3}, 
        --   {1,2,3} 
        -- } 

The first example repeats 0 four times. The second example repeats the sequence { 1, 2, 3 } three times.

The arithmetic operators, like ( * ) for multiplication, operate on each element in turn:

 
    ? { 1, 2, 3 } * 3 
        -- { 3, 6, 9 } 

Addition and subtraction also work the same way.



8.12 Sequence slices



The slice operations work on all types of sequences including strings and general sequences:

 
    include print.e 
    sequence list = { 'a', 'b', 'c', 'd', 'e', 'f' } 
    print(1, list[1..3] ) puts(1, "\n" ) 
    print(1, list[1..4] ) puts(1, "\n" ) 
    print(1, list[3..$] ) puts(1, "\n" ) 
    print(1, list[1..$] ) 
        -- "abc" 
        -- "abdd" 
        -- "cdef" 
        -- "abcdef" 

You may make a copy of a sequence by leaving out the ( [ ] ) brackets:

 
    sequence sample = { 1, 2, 10, 20 } 
    sequence copy 
    copy = sample 
    ? sample 
        -- { 1, 2, 10, 20 } 

A slice operator on the left side of an assignment can update multiple elements:

 
    sequence t = { 'a', 'b', 'c', 'd', 'e', 'f'  } 
    t[1..2] = { 'x', 'y' } 
        -- { 'x', 'y', 'c', 'd', 'e', 'f' } 



8.13 Sequences are mutable



Like strings, sequences are mutable, which means we can change their elements. Using the bracket operator on the left side of an assignment, we can update one of the elements:

 
    include print.e 
    sequence fruit =  {"banana", "apple", "quince"} 
    fruit[1] = "pear" 
    fruit[2] = "orange" 
    print(1, fruit ) 
        -- { "pear", "orange", "quince"} 



8.13.1 must introduce pretty-print here



With the slice operator we can update several elements at once:

 
    include print.e 
    sequence list 
    list = { 'a', 'b', 'c', 'd', 'e', 'f'} 
    list[1..2] =  {'x', 'y'} 
    print(1, list ) 
        -- {xycdef} 

You must take care the the sequence lengths are the same for this update to work.

need more library routnes for following stunts

We can also remove elements from a list by using a slice operation:

 
    include print.e 
    sequence list = { 'a', 'b', 'c', 'd', 'e', 'f' } 
    list = list[4..$] 
    print(1, list) 
        -- "def" 

And we can add elements to a list by concatenation of a sequence at the desired location:

 
    include print.e 
    sequence list1, list2 
    list1 = { 'a', 'd', 'f' } 
    list2 = { 'b', 'c' } 
    list1 = list1[1..1] & list2 & list1[2..$] 
    print(1, list1 ) 
        -- "abcdef" 



8.14 8.9 List deletion



Using slices to delete list elements can be awkward, and therefore error-prone. Python provides an alternative that is more readable. del removes an element from a list:

 
#Code 
>>> a = ['one', 'two', 'three'] 
>>> del a[1] 
>>> a 
['one', 'three'] 
#EndCode 

As you might expect, del handles negative indices and causes a runtime error if the index is out of range.

You can use a slice as an index for del:

 
>>> list = ['a', 'b', 'c', 'd', 'e', 'f'] 
>>> del list[1:5] 
>>> print list 
['a', 'f'] 

As usual, slices select all the elements up to, but not including, the second index.



8.15 List routines



Euphoria provides functions that operate on sequences. For example append() adds a new element to the end of a sequence::

 
    sequence t = { 'a', 'b', 'c' } 
    t = append( t, 'd' ) 
 
    puts(1, t ) 
        -- abcd 
 
    include std.e 
    pretty_print(1, t, {1} ) 
        -- {97'a',98'b',99'c',100'd'} 

You may extend a sequence using the ( & ) operator:

 
    sequence t1 = { 'a', 'b', 'c' } 
    sequence t2 = { 'd', 'e' } 
    puts(1, t1 & t2 ) 
        -- abcde 

 
    include std/sort.e 
 
    sequence t = { 'd', 'c', 'e', 'b', 'a' } 
    t = sort( t ) 
 
    include std.e 
    pretty_print(1, t ) 
        -- {97'a',98'b',99'c',100'd',101'e'} 

By default sort() sorts into ASCENDING order. The argument DECENDING produces the reversed result:

 
    include std.e 
    t = { 'd', 'c', 'e', 'b', 'a' } 
    t = sort( t , DESCENDING ) 
    pretty_print(1, t ) 
        -- {101'e',100'd',99'c',98'b',97'a'} 



8.16 Sequence arguments/parameters



When you pass a Euphoria object as an argument to a routine, you make a copy of that object for use within the routine. When objects are small, say a few atoms, then the amount of duplication is of little concern. But if you pass a sequence, a large sequence, to a routine then you may worry about the dupliction of large amounts of data. In reality Euphoria has internal optimizations that only create a copy when absolutely necessary--at other times a reference to the original is used instead. This gives you both safety (the original object will never be altered) and speed (copies are only make when needed).

For example, the function head takes a sequence as a argument and returns the first element:

 
    function head( sequence list ) 
        return list[1] 
    end function 
    sequence numbers = { 1, 2, 3 } 
    ? head(numbers) 
        -- 1 

Here's how it is used:

The parameter list and the variable numbers are aliases for the same object. The state diagram looks like this:

@Include Graphic stack5.eps

 
    list 
    [ 1, 2, 3 ] 
    numbers __main__ 
    head 

Since the list object is shared by two frames, we drew it between them. In Euphoria, of a function modifies a list parameter, the caller does not sees the change. For example, deleteHead removes the first element from a list:

 
    function deletehead( sequence list ) 
        return list[2..$] 
    end function 
    sequence numbers = { 1, 2, 3 } 
    ? deletehead(numbers) 
        -- 1 

Here's how deleteHead is used:

When a function returns a sequence, it returns a new sequence. For example, tail returns a sequence that contains all but the first element of the given sequence:

Here's how tail is used:

 
    function tail( sequence list ) 
        return list[2..$] 
    end function 
    sequence numbers, rest 
    numbers = {1,2,3} 
    rest = tail(numbers) 
    ? rest 
    ? gets(0) 
        -- {2,3} 

Creating rest, and any subsequent changes to rest, have no effect on numbers.



8.17 Nested lists



A nested sequence is a sequence that appears as an element in another sequence. In this sequence, the three-eth element is a nested sequence:

 
    sequence list = { "hello", 2.0, 5, { 10, 20 } } 

If we print list[3], we get { 10, 20 }. To extract an element from the nested sequence, we can proceed in two steps:

 
    elt = list[3] 
    elt[0] 
    10 

Or we can combine them:

 
    list[3][1] 
    20 

Bracket operators evaluate from left to right, so this expression gets the three-eth element of sequence and extracts the one-eth element from it.



8.18 Matrixes



Nested sequences are often used to represent matrices . For example, the matrix:

 
    1 2 3 
    4 5 6 
    7 8 9 

might be represented as:

 
    sequence matrix 
    matrix = { { 1, 2, 3 }, { 4, 5, 6 } , { 7, 8, 9 } } 

This matrix is a sequence with three elements, where each element is a row of the matrix. We can select an entire row from the matrix in the usual way:

 
    ? matrix[2] 
        -- { { 4, 5, 6 } } 

Or we can extract a single element from the matrix using the double-index form:

 
    ? matrix[2][2] 
        -- 5 

The first index selects the row, and the second index selects the column. Although this way of representing matrices is common, it is not the only possibility. A small variation is to use a list of columns instead of a list of rows. Later we will see a more radical alternative using a map.

A vertical slice can also be extracted:

 
    include std/sequence.e 
 
    sequence vs = vslice( matrix, 3 ) 
    ? vs 
        -- { 3, 6, 9 } 



8.19 Strings and sequences



Two of the most useful functions in the string module involve lists of strings. The split function breaks a string into a list of words. By default, any number of whitespace characters is considered a word boundary:

 
>>> import string 
sequence song 
song = green @Color {''The rain in Spain...''} 
>>> string.split(song) 
 
-- { ''The'', ''rain'', ''in'', ''Spain...''} 

An optional argument called a delimiter can be used to specify which characters to use as word boundaries. The following example uses the string "ai" as the delimiter:

 
>>> string.split(song, 'ai') 
['The r', 'n in Sp', 'n...'] 

Notice that the delimiter doesn't appear in the sequence. The join function is the inverse of split. It takes a sequence of strings and concatenates the elements with a space between each pair:

 
    sequence list 
    list = { ''The'', ''rain'', ''in'', ''Spain...''  } 
    string.join(list) 
 
    The rain in Spain... 

Like split(), join() takes an optional delimiter that is inserted between elements:

 
>>> string.join(list, '_') 
'The_rain_in_Spain...' 



8.20 Map, filter and reduce



To add up all the numbers in a list, you can use a loop like this:

 
    function add_all( sequence t ) 
        atom total = 0 
        for i=1 to length(t) do 
             total += t[i] 
             end for 
        return total 
    end function 

The internal variable total is initialized to 0. Each time through the loop, i is incremented and a new element is added to the value of total. The += operator provides a short way to update a variable:

 
    total += t[i] 

is equivalent to:

 
    total = total + t[i] 

As the loop executes, total accumulates the sum of the elements; a variable used this way is sometimes called accumulator . Adding up the elements of a list is such a common operation that Euphoria provides a built-in function sum():

 
    include std/math.e 
    sequence t = { 1, 2, 3 } 
    ? sum( t ) 
        -- 6 

The function sum() will all all the elements together; including any nested sequence.

An operation like this, that combines a sequence of elements into a single value is sometimes called reduce . Sometimes you want to traverse one sequence while building another. For example, the following function takes a sequence of strings and returns a new sequence that contains capitalized strings:

 
    include std/text.e 
 
    function capitalize_all( sequence t ) 
        sequence res = {} 
        for i=1 to length(t) do 
            res = append( res, proper(t[i] ) ) 
            end for 
        return res 
    end function 
 
    sequence s={"EUPHORIA PROGRAMMING","language","rapid dEPLOYMENT","sOfTwArE"} 
        --{"Euphoria Programming", "Language", "Rapid Deployment", "Software"} 

After being quite proud of your routine, you realize that Euphoria allows operations across entire sequences at once:

 
    include std/text.e 
 
   s = proper({"EUPHORIA PROGRAMMING","language","rapid dEPLOYMENT","sOfTwArE"}) 
     --{"Euphoria Programming", "Language", "Rapid Deployment", "Software"} 

When recycling code from examples written in other languages you may go through the same process. First you make a copy of the code snippet that works like the original. Then you look at your code again, and discover a faster, more Euphoria, way of doing things.

We are demonstrating some typical computer science operations; the long example shows a common pattern found in software. In this example, res, is initialized with an empty sequence; each time through the loop, we append the next element. So res is another kind of accumulator.

An operation like capitalize_all() is sometimes called a map because it "maps" a function (in this case proper() ) onto each of the elements in a sequence.

Another common operation is to select some of the elements from a list and return a sublist. For example, the following function takes a list of strings and returns a list that contains only the uppercase strings:

 
    include std.e 
 
    function only_upper( sequence t ) 
        sequence res = {} 
        for i=1 to length(t) do 
            if equal( t[i], upper( t[i]) ) then 
                res = append( res, t[i]  ) 
            end if 
        end for 
        return res 
    end function 
 
    sequence s = { "mixed", "UPPER", "anD", "Lower" } 
 
    pretty_print(1, only_upper( s ), {2} ) 
 
        -- "UPPER" 

An operation like only_upper() is called a filter because it selects some of the elements and filters out the others.

Most common sequence operations can be expressed as a combination of map, filter, and reduce. Because these operations are so common, Euphoria provides routines in the library that can make them easier to write.



8.21 Deleting elements



There are several ways to delete elements from a sequence.

 
    sequence t = { 'a', 'b', 'c' } 
    t = remove( t, 2 ) 
 
    include std.e 
    pretty_print(1, t ) 
        --{97'a', 99'c'} 

You may also remove a slice from a sequence:

 
    sequence s = "All for one" 
    s = remove(s, $-3, $ ) 
    puts(1, s ) 
        -- All for 

It is possible to replace one element(s) with another:

png/08_replace.png

 
    sequence title = "It was a dark and stormy night." 
    atom x = match( "stormy", title ) 
    title = replace(title, "calm ", x, x+6 ) 
    puts(1, title ) 
        -- It was a dark and calm night. 

There is a full set of sequence operations that include: remove(), splice(), remove_all(), and insert().



8.22 Sequences and strings

A string is a sequence of characters. A list is a sequence of values. Since they are both sequences, they are completely interchangeable:

 
    include std.e 
 
    sequence s = "spam" 
    puts(1, s ) 
    pretty_print(1, s ) 
        -- spam 
        -- {115's',112'p',97'a',109'm'} 

The only distinction is how you choose to display the values.

A string could be broken into words if you choose.

 
    include std.e 
 
    sequence s = "pining for the fjords" 
    s = split( s ) 
 
    include std.e 
    pretty_print(1, s, {2} ) 
        --{ 
        --  "pining", 
        --  "for", 
        --  "the", 
        --  "fjords" 
        -- } 

By default, it is assumed that ( ' ' ) and empty space is used to separate, or delimit, one word from another.

You may choose the exact delimeter used for splitting a string. The following example uses a hyphen as a delimiter:

 
    s = "spam-spam-spam" 
    atom delimit = '-' 
    s = split( s, delimit ) 
    pretty_print(1, s, {2} ) 
 
    --{ 
    --"spam", 
    --"spam", 
    --"spam" 
    -- } 

The function join() is the inverse of split(). It takes a sequence of strings and concatenates the elements using delimiter to join the strings together.

 
    include std.e 
    sequence t = { "pining", "for", "the", "fjords" } 
    object delimiter = '*' 
    t = join( t, delimiter ) 
    puts(1, t ) 
        --pining*for*the*fjords 

The default delimiter is a blank space ( ' ' ), but any object may be used.



8.23 Debugging


one off-by-one errors

conventional: starts at zero

Eu starts at one


back next




8.24 Glossary