# Rebol Programming/Language Features/Series
## Introducing series

Series were introduced to Rebol by its designer to represent sequences of values. The most typical series in Rebol are blocks.
Naturally, it would be quite nifty if you could access, manipulate, and refer to individual entries within a series.
Well, it just so happens that Rebol provides a quite extensive "data access technology" for accomplishing this.
Let's look at a first example which will help to sharpen our intuitive idea as to what a series looks like.
The following is a Rebol block which has two entries:
You might read this as
Since any block has zero or more entries (you did know that a block can have nothing in it, n'est-ce pas?), and since as a point of fact this particular block happens to have 2 entries, you would be quite correct to say that the block can be treated as a series.
And of course, looking at this, you might wonder, how do you do things like "ask" what the second entry's value is? Or maybe you want to know if the n'th entry in a particular series exists? Or maybe you wish to change a particular entry within a series.
You can do all this and much more in Rebol.
So let's take a look at how we can access data in a series within Rebol.

## Data access methods

In Rebol we can use several methods. The method using the first, second, etc. functions:
The method using the path notation:
The method using the pick function:

### Using variables for data access

Let's suppose, that we have got a variable (e.g. i),
that shall be used to access a specific entry within a series. How can this be done?
Although the method using the first function doesn't look useful for this, it can be used too. The details are described in the subsequent sections.
The method using the path notation is:
The method using the pick function:

### Why series/i doesn't work?

Actually, series/i does work, but it doesn't interpret the word i as a variable. Example:
Description: when the interpreter encountered the series23/i expression, it tried to find the word i in series23, i.e. it took the word as a value, not as a variable. The value returned is the value next to the value, that has been found.
But since core 2.6.0 series/(i) is implemented:
By placing the word i into parentheses, the parenthesized expression is evaluated and the value of the third entry of the series is returned.

## Changing series

Sometimes we need to change a series. Let's suppose, that we want to change series1. The first method uses the change function:
The path notation method:
The poke function method:

### Using variables

If we have got a variable i as above, we can still use the change method. The details are discussed in the subsequent sections.
The path notation method:
The poke method:

## Data access method differences

The three data access methods listed above exhibit different behaviour in special situations. Let's prepare series2 to see the differences:

### The first function access method

When we try to access an entry that doesn't exist, an error is caused:
When we access a function using this method, the function is the result:

### The path notation access method

When we try to get the value of an entry that doesn't exist, we obtain the none value:
When we access a function using the path method, the function is evaluated:

### The pick function access method

When we try to get the value of an entry that doesn't exist, we obtain the none value:
When we access a function using the pick method, the function is the result:

### Why does Rebol need so many access methods?

Actually it doesn't, but the methods work differently as has been shown and that is why we can choose the method that is the most appropriate for our task.
If we prefer the most strict bound checking, we may prefer first.
If we need automatic evaluation of functions or automatic searching, we may prefer the path access.
If we want a simple access method, that doesn't evaluate functions, we may choose pick.

## Length of a series

A series can have many entries. It may be useful to know the actual number for a given series. We can use the length? native to find it:
On the other hand, it is even possible, that a series does not have any entry. There is a Rebol native empty? which tells us, whether a series "does not have any entry". In that case the length? native yields zero:

## From head to tail and back again

Sometimes we need to access series sequentially instead of using indexed access. Rebol series can be accessed sequentially without a need to use any additional datatype.
To move sequentially "one entry at a time" in a non-empty series we can use the next native. The next native yields a series with the first entry "skipped". The second entry of the original series becomes the first entry of the new series.
The first, second, etc. entries of the new series are the same, as the second, third, etc. entries of the original series:
In the new series we can use even the skipped entries: (R2 only)
The length of the new series is equal to the length of the original series minus one:
Instead of skipping one entry at a time, we can skip more entries at once using the skip action. A Rebol description of the skip action for non-negative offset:
If we skip zero entries, we obtain the original series:
If we skip all entries of a series, we obtain an empty series called its tail:
Here is a definition of a function that determines whether a series is a tail:
Skipping further from tail using the next function, doesn't have any effect:
We can use the back action to skip one entry backwards, which means the reverse of next:
Let's enhance the skip-def to use negative offset values:
The series we obtain from a given series by skipping the maximum possible number of entries backwards is called the head of the given series. Any subsequent back call doesn't skip any further and yields the head too:
We can use the above definition to provide a Rebol function determining if a series is a head:
With a help of head? we can even provide the Rebol description of head:
The number of the "backward" entries increased by one can be obtained as a result of the index? native:

## Referring to values

Let's discover a less natural property of series. A series can refer twice (or more times) to a given value. The following function creates a block referring to a given value exactly twice:
Let's test the properties of the function we defined:
By analogy, one value can be referenced by two series:

## Properties of the change action

If we want to change a series, we can use the change action. Change can be said to change the series; it causes the affected entry to refer to another value. On the other hand, change doesn't affect the values:
change doesn't return the original series. It skips the affected entries to facilitate subsequent changes.

## Comparing entries and series

For any series we can use the copy action to create a new series referring to the same values. A copy of a series is not the same series as the original, though:
We have seen an example of a situation, when two distinct series entries refer to one Rebol value, or a situation, when two distinct series (prepared using e.g. the skip function) share some entries. Although series entries do not behave as stand-alone entities in Rebol, we can specify an individual entry by specifying a series and a number of the entry in the series. Thus, we can find out, if two entries (references) specified are the same. The function doing this can be found at References.
Since we are able to compare entries, we can say, that non-empty series having the same datatype are the same, if they consist of the same entries. The trouble with this method is, that it is not usable for empty series, because in an empty series there is no entry we could use for the comparison purposes.
We call two series mutually independent, if they neither share any entries, nor they can be derived from one "mother" series using the skip function. In Rebol this definition can be written as follows:
Observation: a series and its copy are mutually independent.

## Cumulative properties of series operations

A cumulative property of change/only: change/only changes the reference of its argument Series. From this description is immediately obvious, that change/only affects all series sharing the reference in question.
We can create a new reference using the insert action.
We can use insert to define the meaning of the same? action for empty series. We say, that two empty series are the same, if insert used for the first series causes the second series to become non-empty too.
The description of the insert/only work: insert/only defines a new entry in the series, enlarging the number of entries in the series by one. The newly inserted entry becomes the first entry of the series, while the numbers of the other entries in the series are therefore higher by one. insert/only causes the newly created entry to refer to its value argument. Note, that insert/only doesn't change Index of any series! It skips the inserted entry to facilitate subsequent inserts.
The cumulative property of insert/only: for all series dependent on the series affected by insert/only holds: the dependent series is enlarged by one entry too, moreover, if the index of the dependent series is lower than the index of the affected series, its first entry remains the same, if the index of the dependent series is equal to the index of the affected series, the first entry of the series after insert will be the newly inserted reference and if the index of the dependent series is higher than the index of the affected series, then the first entry of the dependent series will be the entry preceding the original first entry.
For lowering the number of entries in a series we can use the remove and clear actions. Probably every reader is now able to use the above described methods for exploring the cumulative properties of these. I shall leave that as an exercise for the reader.

## Series with fast index access versus series with fast remove/insert operations
### Series with fast index access

Rebol blocks are examples of series with fast index access. That means, that operations like
have the same speed regardless of:
* the length of the series
* the value of the index
Generally, all Rebol series except for Rebol lists are of this type.

#### Advantages

* the speed of index access (pick, poke, skip, at, length?, index?, offset?, etc.)

#### Disadvantages

* general remove/insert operations are "slow", the time needed to perform a remove/insert operation is proportional to the length of the series in general case
* after any insert/remove operation the index of all related series remains the same, which means, that after an insert or a remove operation a series actually refers to different entries than before such an operation:
* due to the above property, it is even possible to obtain past-tail series, i.e. series having an index that is actually not "available" in the given series

### Series with fast insert/remove

As already mentioned, there is only one Rebol series type having this property. It is the list! datatype.

#### Advantages

* remove/insert operations are fast
* after an insert operation, all Rebol lists remain "unaffected" in that they point to the entry they pointed to before the insert operation was performed
* after a remove operation, all Rebol lists (except for the ones directly affected, having a removed entry as the first) remain "unaffected" in that they point to the entry they did point to before the remove operation was performed
* no list can be caused to become past-tail

#### Disadvantages

* due to the way how Rebol lists are implemented in R2 currently, a removal of the first element of a list causes the list to become a "list having an invalid (removed) position"; but see my list prototype below coping with this problem without a significant slow-down of any operation; the prototype adjusts such lists to point to the first subsequent valid entry instead of the removed one
* general index operations are "slow"; time needed to perform such operations is proportional to the length of the list in the general case

#### List prototype

Since lists are not present in R3, and due to the above mentioned problem with removed entries I decided to implement a list prototype that can improve the situation.