"A nonempty, immutable sequence of values. The type
`Sequence<Element>` may be abbreviated `[Element+]`.
Given a possibly-empty sequence of type `[Element*]`, the
`if (nonempty ...)` construct, or, alternatively,
`assert (nonempty ...)`, may be used to narrow to a
sequence type to a nonempty sequence type:
[Integer*] nums = ... ;
if (nonempty nums) {
Integer first = nums.first;
Integer max = max(nums);
[Integer+] squares = nums.collect((Integer i) => i**2));
[Integer+] sorted = nums.sort(byIncreasing((Integer i) => i));
}
Operations like `first`, `max()`, `collect()`, and `sort()`,
which polymorphically produce a nonempty or non-null output
when given a nonempty input are called
_emptiness-preserving_.
`Sequence` has the following subtypes:
- [[ArraySequence]], a sequence backed by an [[Array]],
- [[Range]], an efficient representation of a sequence of
adjacent [[enumerable values|Enumerable]],
- [[Tuple]], a typed linked list, and
- [[Singleton]], a sequence of just one element."
see (`interface Empty`,
`class ArraySequence`,
`class Range`,
`class Tuple`,
`class Singleton`)
by ("Gavin")
tagged("Sequences")
shared sealed interface Sequence<out Element=Anything>
satisfies Element[] &
{Element+} {
"The first element of the sequence, that is, the element
with index `0`."
shared actual formal Element first;
"The last element of the sequence, that is, the element
with index `sequence.lastIndex`."
shared actual formal Element last;
"Returns `false`, since every `Sequence` contains at
least one element."
shared actual Boolean empty => false;
"The non-negative length of this sequence, that is, the
number of elements in this sequence."
shared actual formal Integer size;
"The index of the last element of the sequence."
see (`value size`)
shared actual default Integer lastIndex => size-1;
"An integer [[Range]] containing all indexes of this
sequence, that is, the range `0..sequence.lastIndex`."
shared actual default Range<Integer> keys => indexes();
"An integer [[Range]] containing all indexes of this
sequence, that is, the range `0..sequence.lastIndex`."
shared actual default Range<Integer> indexes()
=> 0..lastIndex;
"This nonempty sequence."
shared actual default [Element+] sequence() => this;
since("1.3.3")
shared actual default [Element+] tuple()
=> arrayToTuple(Array(this));
"The rest of the sequence, without the first element."
shared actual default Element[] rest
=> size > 1
then Subsequence(1, lastIndex)
else [];
"This sequence, without the last element."
since("1.3.3")
shared actual default Element[] exceptLast
=> size > 1
then Subsequence(0, lastIndex-1)
else [];
since("1.3.3")
shared actual default
Element[] sublist(Integer from, Integer to)
=> from<=to && from<=lastIndex && to>=0
then Subsequence {
from = Integer.largest(0, from);
to = Integer.smallest(lastIndex, to);
}
else [];
since("1.3.3")
shared actual default
Element[] sublistTo(Integer to)
=> sublist(0, to);
since("1.3.3")
shared actual default
Element[] sublistFrom(Integer from)
=> sublist(from, size-1);
"A sequence containing the elements of this sequence in
reverse order to the order in which they occur in this
sequence."
shared default actual [Element+] reversed => Reverse();
"Produces a sequence formed by repeating the elements of
this sequence the given [[number of times|times]], or
the [[empty sequence|empty]] if `times<=0`."
shared default actual Element[] repeat(Integer times)
=> switch (times<=>1)
case (smaller) []
case (equal) this
case (larger) Repeat(times);
"This nonempty sequence."
shared actual default [Element+] clone() => this;
/*shared actual default Element[] repeat(Integer times) {
value resultSize = size*times;
value array = arrayOfSize(resultSize, first);
variable value i = 1;
while (i < resultSize) {
array[i] = getElement(i%size);
}
return ArraySequence(array);
}*/
"A nonempty sequence containing the elements of this
container, sorted according to a function imposing a
partial order upon the elements."
shared default actual
[Element+] sort(
"The function comparing pairs of elements."
Comparison comparing(Element x, Element y)) {
value array = Array(this);
array.sortInPlace(comparing);
return ArraySequence(array);
}
"A nonempty sequence containing the results of applying
the given mapping to the elements of this sequence."
shared default actual
[Result+] collect<Result>(
"The transformation applied to the elements."
Result collecting(Element element)) {
object list
extends Object()
satisfies List<Result> {
lastIndex => outer.lastIndex;
size = outer.size;
getFromFirst(Integer index)
=> if (0<=index<size)
then collecting(outer.getElement(index))
else null;
}
return ArraySequence(Array(list));
}
"Return a nonempty sequence containing the given
[[element]], followed by the elements of this
sequence."
shared actual default
[Other,Element+] withLeading<Other>(Other element)
=> [element, *this];
"Return a nonempty sequence containing the elements of
this sequence, followed by the given [[element]]."
shared actual default
[Element|Other+] withTrailing<Other>(Other element)
=> JoinedSequence(this, Singleton(element));
"Return a nonempty sequence containing the elements of
this sequence, followed by the given [[elements]]."
shared actual default
[Element|Other+] append<Other>(Other[] elements)
=> if (nonempty elements)
then JoinedSequence(this, elements)
else this;
"Return a nonempty sequence containing the given
[[elements]], followed by the elements of this
sequence."
shared actual default
[Element|Other+] prepend<Other>(Other[] elements)
=> if (nonempty elements)
then JoinedSequence(elements, this)
else this;
shared actual default
Boolean contains(Object element)
=> (super of List<Element>).contains(element);
shared actual default
Boolean shorterThan(Integer length)
=> (super of List<Element>).shorterThan(length);
shared actual default
Boolean longerThan(Integer length)
=> (super of List<Element>).longerThan(length);
shared default actual
Element? find(Boolean selecting(Element&Object elem))
=> (super of List<Element>).find(selecting);
shared default actual
Element? findLast(Boolean selecting(Element&Object elem))
=> (super of List<Element>).findLast(selecting);
shared actual default
Element[][2] slice(Integer index)
=> [this[...index-1], this[index...]];
shared actual default
Element[] span(Integer from, Integer to) {
if (from <= to) {
return
if (to < 0 || from > lastIndex)
then []
else if (from <= 0 && to >= lastIndex)
then this
else
ArraySequence(Array(sublist(from, to)));
}
else {
return
if (from < 0 || to > lastIndex)
then []
else if (to <= 0 && from >= lastIndex)
then reversed
else
ArraySequence(Array(sublist(to, from).reversed));
}
}
shared actual default
Element[] spanFrom(Integer from)
=> from<size then span(from, size-1) else [];
shared actual default
Element[] spanTo(Integer to)
=> to>=0 then span(0, to) else [];
shared actual default
Element[] measure(Integer from, Integer length)
=> length > 0
then span(from, from+length-1)
else [];
shared actual default
String string => (super of Sequential<Element>).string;
Element getElement(Integer index) {
if (exists element = getFromFirst(index)) {
return element;
}
else {
assert (is Element null);
return null;
}
}
class Reverse()
extends Object()
satisfies [Element+] {
size => outer.size;
first => outer.last;
last => outer.first;
reversed => outer;
function outerIndex(Integer index) => size-1-index;
getFromFirst(Integer index)
=> outer.getFromFirst(outerIndex(index));
span(Integer from, Integer to)
=> outer[outerIndex(from)..outerIndex(to)];
iterator()
=> let (outerList = outer)
object satisfies Iterator<Element> {
variable value index = outerIndex(0);
next() => index<0
then finished
else outerList.getElement(index--);
string => "``outer.string``.iterator()";
};
}
class Repeat(Integer times)
extends Object()
satisfies [Element+] {
assert (times>1);
last => outer.last;
first => outer.first;
size => outer.size*times;
//rest => outer.rest.append(outer.repeat(times-1));
getFromFirst(Integer index)
=> let (size = outer.size)
if (index<size*times)
then outer.getFromFirst(index%size)
else null;
iterator() => CycledIterator(outer,times);
}
class Subsequence(Integer from, Integer to)
extends Object()
satisfies [Element+] {
assert (from>=0, to>=0, from<=to);
shared actual Element first {
if (exists first = outer[from]) {
return first;
}
else {
assert (is Element null);
return null;
}
}
shared actual Element last {
if (exists last = outer[to]) {
return last;
}
else {
assert (is Element null);
return null;
}
}
size => to-from+1;
rest => size == 1 then []
else outer.Subsequence(from+1, to);
exceptLast => size == 1 then []
else outer.Subsequence(from, to-1);
getFromFirst(Integer index)
=> if (0<=index<=to-from)
then outer.getFromFirst(index+from)
else null;
iterator() => outer.take(to+1).skip(from).iterator();
sublist(Integer from, Integer to)
=> outer.sublist {
from = Integer.largest(from+this.from,this.from);
to = Integer.smallest(to+this.from,this.to);
};
span(Integer from, Integer to)
=> from<=to
then outer.span {
from = Integer.largest(from+this.from,this.from);
to = Integer.smallest(to+this.from,this.to);
}
else outer.span {
from = Integer.smallest(from+this.from,this.to);
to = Integer.largest(to+this.from,this.from);
};
}
}
"A [[nonempty sequence|Sequence]] of the given [[elements]],
or `null` if the given stream is empty. A non-null, but
possibly empty, [[sequence|Sequential]] may be obtained
using the `else` operator:
[Element*] sequenceOfElements = sequence(elements) else [];"
by ("Gavin")
see (`function Iterable.sequence`)
tagged("Sequences")
since("1.1.0")
deprecated("Since 1.3.2, [[Iterable.sequence]] has a more
precise return type, and so this function is no
longer useful.")
shared [Element+]|Absent sequence<Element,Absent=Null>
(Iterable<Element, Absent> elements)
given Absent satisfies Null {
if (nonempty sequence = elements.sequence()) {
return sequence;
}
else {
assert (is Absent null);
return null;
}
}
class JoinedSequence<Element>
([Element+] firstSeq, [Element+] secondSeq)
extends Object()
satisfies [Element+] {
size => firstSeq.size + secondSeq.size;
first => firstSeq.first;
last => secondSeq.last;
//rest => firstSeq.rest.append(secondSeq);
getFromFirst(Integer index)
=> let (cutover = firstSeq.size)
if (index<cutover)
then firstSeq.getFromFirst(index)
else secondSeq.getFromFirst(index-cutover);
slice(Integer index)
=> if (index==firstSeq.size)
then [firstSeq, secondSeq]
else super.slice(index);
spanTo(Integer to)
=> if (to==firstSeq.size-1)
then firstSeq
else super.spanTo(to);
spanFrom(Integer from)
=> if (from==firstSeq.size)
then secondSeq
else super.spanFrom(from);
shared actual
Element[] measure(Integer from, Integer length) {
if (from==0 &&
length==firstSeq.size) {
return firstSeq;
}
else if (from==firstSeq.size &&
length>=secondSeq.size) {
return secondSeq;
}
else {
return super.measure(from, length);
}
}
shared actual
Element[] span(Integer from, Integer to) {
if (from<=0 &&
to==firstSeq.size-1) {
return firstSeq;
}
else if (from==firstSeq.size &&
to>=size-1) {
return secondSeq;
}
else {
return super.span(from, to);
}
}
iterator() => ChainedIterator(firstSeq,secondSeq);
}