Sequences are a special type in the language and can be thought of as a collection of elements, similar to an array.
In contrast to arrays, sequences do not imply a specific form of data storage, but can represent any collection of elements that is accessible in a specific order. This could be an array (and as a matter of fact, all arrays can be treated as a sequence) or a different data store, such as a linked list, a binary tree or a custom collection implementation.
Sequences can also represent non-static data that is retrieved or generated on the fly, as the sequence is enumerated ? for example, you could implement a sequence that calculates all digits of Pi, or retrieves RSS headlines downloaded from a server.
In Oxygene, sequences are represented by the "sequence of" keyword (analogous to the "array of" syntax), so a variable holding a sequence of strings would be defined as follows:
var Names: sequence of String;
A variable defined like this would then be capable of holding any type of sequence ? be it an array, a standard generic List<String> collection class or a custom sequence implementation.
Because sequences are an abstract type concept that represents the concept of how data is accessed, but not the way the data is stored (unlike, for example arrays), you cannot directly instantiate a "sequence", in the way that you would instantiate an array. Instead, you will usually instantiate a specific type that implements sequence behavior. For example, the following assignments would be valid:
Names := ['Peter', 'Paul']; // arrays are sequences, too Names := new List<String>; // the List type is a sequence Names := GatherData(); // call a function that returns a sequence
Once an instance of a sequence is obtained, it can be used in a variety of ways, regardless of the way the actual data is stored or generated. Most importantly, it can be looped over using the "for each" construct, or accessed using the query operations and Standard Query Operators that will be discussed later in this chapter.
Once again, it is important to realize that the sequence merely defines a front-end for accessing data, but is entirely decoupled from the way this data exists. If a sequence references an array or List object, it will of course directly access the in memory data contained by this array or list. However, if the sequence references a custom implementation or an iterator, data might be fetched from external sources or generated on-the-fly, as needed.
In this second case, individual elements of the sequence will not be obtained until code is used to access and enumerate the sequence, meaning that little or no cost will be involved in generating the initial sequence reference. For example, a sequence backed by a database (such as the DLinq tables we will learn more about later) might contain hundreds or even thousands of records ? but these records will not be transferred from the database server into local memory until the individual rows are accessed.
var r: queryable sequence of Customer := from x in Tables.Customers where x.Name = 'ALKI' select x;
A queryable sequence is an alias to System.Linq.IQueryable<T>. Queryable sequences are special kinds of sequence where the lambda expressions are turned into an expression tree instead of code, so it can be transported over a network. .NET comes with MSSQL support for LINQ that uses this; also Data Abstract comes with a LINQ provider that can use this feature.
Parallel sequences are a new feature in Oxygene that makes use of the Microsoft Parallel Framework. Parallel sequence are executed over multiple threads when they're called. They are aliases to the System.Linq.IParallelEumerable<T> type.
var r: parallel sequence of string := Enumerable.Range(0, 1000).AsParallel();
Sequences support the + operator when defined as a sequence of T in Oxygene. The + operator will call the concat method to create a new enumerable that returns two lists sequentially. This operator works on all sequence types.