"A [[Range]] of adjacent [[Enumerable]] values generated by
a [[first]] element, and a strictly positive [[size]]. The
range includes all values whose offset from `first` is
non-negative and less than the `size`."
see (`class Span`,
`interface Enumerable`)
final class Measure<Element>(first, size)
extends Range<Element>()
given Element satisfies Enumerable<Element> {
"The start of the range."
shared actual Element first;
"The size of the range."
shared actual Integer size;
"Can't be used for empty segments"
assert (size > 0);
shared actual String string
=> first.string + ":" + size.string;
shared actual Element last => first.neighbour(size-1);
"Determines if this sized range has more elements than
the given [[length]]."
shared actual Boolean longerThan(Integer length)
=> size > length;
"Determines if this sized range has fewer elements than
the given [[length]]."
shared actual Boolean shorterThan(Integer length)
=> size < length;
"The index of the end of the sized range."
shared actual Integer lastIndex => size-1;
"The rest of the range, without its first element."
shared actual Element[] rest
=> size==1 then []
else Measure(first.successor,size-1);
"The element of the range that occurs [[index]] values
after the start of the range."
shared actual Element? getFromFirst(Integer index) {
if (index<0 || index >= size) {
return null;
}
return first.neighbour(index);
}
shared actual Boolean increasing => true;
shared actual Boolean decreasing => false;
"An iterator for the elements of the sized range."
shared actual Iterator<Element> iterator() {
object iterator
satisfies Iterator<Element> {
variable value count = 0;
variable value current = first;
shared actual Element|Finished next() {
if (++count>size) {
return finished;
}
else {
return current++;
}
}
string => "(``outer.string``).iterator()";
}
return iterator;
}
shared actual {Element+} by(Integer step) {
"step size must be greater than zero"
assert (step > 0);
return step == 1 then this else By(step);
}
class By(Integer step)
satisfies {Element+} {
size => 1 + (outer.size - 1) / step;
first => outer.first;
string => "(``outer.string`` by ``step``)";
shared actual Iterator<Element> iterator() {
object iterator
satisfies Iterator<Element> {
variable value count = 0;
variable value current = first;
shared actual Element|Finished next() {
if (++count>size) {
return finished;
}
else {
value result = current;
current = current.neighbour(step);
return result;
}
}
string => "``outer.string``.iterator()";
}
return iterator;
}
}
shared actual Measure<Element> shifted(Integer shift) {
if (shift==0) {
return this;
}
else {
return Measure(first.neighbour(shift),size);
}
}
"Determines if this range includes the given object."
shared actual Boolean contains(Object element) {
if (is Element element) {
return containsElement(element);
}
else {
return false;
}
}
"Determines if this range includes the given value."
shared actual Boolean occurs(Anything element) {
if (is Element element) {
return containsElement(element);
}
else {
return false;
}
}
shared actual Boolean containsElement(Element x)
=> 0 <= x.offset(first) < size;
shared actual Boolean includes(List<Anything> sublist) {
if (sublist.empty) {
return true;
}
else if (is Range<Element> sublist) {
return includesRange(sublist);
}
else {
return super.includes(sublist);
}
}
shared actual Boolean includesRange(Range<Element> sublist) {
switch (sublist)
case (is Measure<Element>) {
value offset = sublist.first.offset(first);
return offset >= 0 && offset + sublist.size <= size;
}
case (is Span<Element>) {
if (sublist.decreasing) {
return false;
}
else {
value offset = sublist.first.offset(first);
return offset >= 0 && offset + sublist.size <= size;
}
}
}
shared actual Boolean equals(Object that) {
if (is Measure<Object> that) {
//optimize for another Measure
return that.size==size && that.first==first;
}
else if (is Span<Object> that) {
return that.increasing &&
that.first == first && that.size == size;
}
else {
//it might be another sort of List
return super.equals(that);
}
}
shared actual Element[] measure(Integer from, Integer length) {
if (length<=0) {
return [];
}
else {
value len = from+length < size then length
else size-from;
return Measure(first.neighbour(from),len);
}
}
shared actual Element[] span(Integer from, Integer to) {
if (from<=to) {
if (to<0 || from>=size) {
return [];
}
else {
value len = to < size then to-from+1
else size-from;
return Measure(first.neighbour(from),len);
}
}
else {
if (from<0 || to>=size) {
return [];
}
else {
value len = from < size then from-to+1
else size-to;
return Measure(first.neighbour(to),len).reversed;
}
}
}
shared actual Element[] spanFrom(Integer from) {
if (from <= 0) {
return this;
}
else if (from < size) {
return Measure(first.neighbour(from),size-from);
}
else {
return [];
}
}
shared actual Element[] spanTo(Integer to) {
if (to<0) {
return [];
}
else if (to < size-1) {
return Measure(first,to);
}
else {
return this;
}
}
}
"Produces a [[Range]] of adjacent [[Enumerable]] values
generated by a [[first]] element, and a strictly positive
[[size]], or returns the [[empty sequence|empty]] if
`size <= 0`. The range includes all values whose offset
from `first` is non-negative and less than the `size`.
More precisely, if `x` and `first` are of `Enumerable`
type `X`, and `size` is an integer, then `x in first:size`
if and only if `0 <= x.offset(first) < size`.
The _measure operator_ `:` is an abbreviation for
`measure()`:
for (i in start:size) { ... }
for (char in '0':10) { ... }
The measure operator accepts the first index and size of
the range:
0:5 // [0, 1, 2, 3, 4]
If the size is nonpositive, the range is empty:
0:0 // []
5:0 // []
0:-5 // []"
shared Range<Element>|[] measure<Element>
(Element first, Integer size)
given Element satisfies Enumerable<Element>
=> size <= 0 then [] else Measure(first, size);