ceylon.language.Span.ceylon Maven / Gradle / Ivy
"A [[Range]] of adjacent [[Enumerable]] values generated by
two endpoints: [[first]] and [[last]]. The range includes
both endpoints, and all values falling _between_ the
endpoints."
by ("Gavin")
see (`class Measure`,
`interface Enumerable`)
final serializable
class Span(first, last)
extends Range()
given Element satisfies Enumerable {
"The start of the range."
shared actual Element first;
"The end of the range."
shared actual Element last;
string => first.string + ".." + last.string;
increasing = last.offsetSign(first) >= 0;
decreasing => !increasing;
"Determines if the range is of recursive values, that
is, if successors wrap back on themselves. All
recursive ranges are [[increasing]]."
Boolean recursive
= first.offsetSign(first.successor) > 0 &&
last.predecessor.offsetSign(last) > 0;
Element next(Element x)
=> increasing
then x.successor
else x.predecessor;
Element nextStep(Element x, Integer step)
=> increasing
then x.neighbour(step)
else x.neighbour(-step);
Element fromFirst(Integer offset)
=> increasing
then first.neighbour(offset)
else first.neighbour(-offset);
Boolean afterLast(Element x)
=> increasing
then x.offsetSign(last) > 0
else x.offsetSign(last) < 0;
Boolean beforeLast(Element x)
=> increasing
then x.offsetSign(last) < 0
else x.offsetSign(last) > 0;
Boolean beforeFirst(Element x)
=> increasing
then x.offsetSign(first) < 0
else x.offsetSign(first) > 0;
Boolean afterFirst(Element x)
=> increasing
then x.offsetSign(first) > 0
else x.offsetSign(first) < 0;
shared actual Integer size {
value lastIndex = last.offset(first).magnitude;
if (lastIndex if (length < 1) then true
else if (recursive) then size > length
else beforeLast(fromFirst(length - 1));
shorterThan(Integer length)
=> if (length < 1) then true
else if (recursive) then size < length
else afterLast(fromFirst(length - 1));
lastIndex => size - 1;
rest => first == last then [] else next(first)..last;
"This range in reverse, with [[first]] and [[last]]
interchanged.
For any two range endpoints, `x` and `y`:
`(x..y).reversed == y..x`
except for [[recursive]] ranges, where the elements are
evaluated and collected into a new sequence."
//TODO: we should have a way to produce a decreasing
// recursive range
shared actual
[Element+] reversed
=> recursive
then super.reversed
else last..first;
"The element of the range that occurs [[index]] values
after the start of the range."
shared actual
Element? getFromFirst(Integer index) {
if (index < 0) {
return null;
} else if (recursive) {
return index < size then fromFirst(index);
} else {
value result = fromFirst(index);
return !afterLast(result) then result;
}
}
"An iterator for the elements of the range. The returned
iterator produces elements from [[first]] and continues
producing elements until it reaches an element whose
`offset` from [[last] is zero."
shared actual
Iterator iterator()
=> object
satisfies Iterator {
variable Boolean firstTime = true;
variable Element|Finished element = first;
shared actual Element|Finished next() {
if (!is Finished c = element) {
Element result;
if (firstTime) {
firstTime = false;
result = c;
} else {
result = outer.next(c);
}
if (result.offset(last) == 0) {
this.element = finished;
} else {
this.element = result;
}
return result;
} else {
return element ;
}
}
string => "(``outer``).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);
}
shifted(Integer shift)
=> shift == 0
then this
else Span(first.neighbour(shift),
last.neighbour(shift));
containsElement(Element x)
=> recursive
then x.offset(first) <= last.offset(first)
else !afterLast(x) && !beforeFirst(x);
shared actual
Integer count(Boolean selecting(Element element)) {
variable value element = first;
variable value count = 0;
while (containsElement(element)) {
if (selecting(element)) {
count++;
}
element = next(element);
}
return count;
}
shared actual
Boolean includesRange(Range range) {
switch (range)
case (is Span) {
if (recursive) {
return range.first.offset(first) < size &&
range.last.offset(first) < size;
} else {
return increasing == range.increasing &&
!range.afterFirst(first) &&
!range.beforeLast(last);
}
}
case (is Measure) {
if (decreasing) {
return false;
} else {
value offset = range.first.offset(first);
return 0 <= offset <= size - range.size;
}
}
}
shared actual
Boolean equals(Object that) {
if (is Span that) {
//optimize for another Span
return that.first == first && that.last == last;
} else if (is Measure that) {
return increasing &&
that.first == first && that.size == size;
} else {
//it might be another sort of List
return super.equals(that);
}
}
class By(Integer step)
satisfies {Element+} {
size => 1 + (outer.size - 1) / step;
first => outer.first;
string => "(``outer``).by(``step``)";
shared actual
Iterator iterator() {
if (recursive) {
return object
satisfies Iterator {
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``.iterator()";
};
} else {
return object
satisfies Iterator {
variable Element|Finished current = first;
variable value firstTime = true;
shared actual Element|Finished next() {
if (firstTime) {
firstTime = false;
return current;
} else {
if (is Element c=current) {
value r = nextStep(c, step);
if (!containsElement(r)) {
current = finished;
} else {
current = r;
}
}
return current;
}
}
string => "``outer``.iterator()";
};
}
}
}
shared actual
[Element*] measure(Integer from, Integer length)
=> length <= 0
then []
else span(from, from + length - 1);
shared actual
[Element*] span(Integer from, Integer to) {
if (from <= to) {
if (to < 0 || !longerThan(from)) {
return [];
} else {
return (this[from] else first)..(this[to] else last);
}
} else {
if (from < 0 || !longerThan(to)) {
return [];
} else {
value range = (this[to] else first)..(this[from] else last);
return range.reversed;
}
}
}
shared actual
[Element*] spanFrom(Integer from) {
if (from <= 0) {
return this;
} else if (longerThan(from)) {
assert (exists first = this[from]);
return first..last;
} else {
return [];
}
}
shared actual
[Element*] spanTo(Integer to) {
if (to < 0) {
return [];
} else if (longerThan(to + 1)) {
assert (exists last = this[to]);
return first..last;
} else {
return this;
}
}
shared actual void each(void step(Element element)) {
variable value current = first;
while (true) {
step(current);
if (current.offset(last)==0) {
break;
}
else {
current = next(current);
}
}
}
}
"Produces a [[Range]] of adjacent [[Enumerable]] values
generated by two endpoints: [[first]] and [[last]]. The
range includes both endpoints, and all values falling
_between_ the endpoints.
- For a recursive enumerable type, a value falls between
the endpoints if its [[offset|Enumerable.offset]] from
`first` is less than the offset of `last` from `first`.
- For a linear enumerable type, a value falls between the
endpoints if the
[[sign of its offset|Enumerable.offsetSign]] from `first`
is the same as the sign of the offset of `last` from
`first` and the sign of its offset from `last` is the
opposite of the sign of the offset of `last` from `first`.
More precisely, if `x`, `first`, and `last` are of
`Enumerable` type `X`, then `x in first..last` if and
only if:
- `X` is recursive and `x.offset(first)=0` the range is increasing,
but
- if `last.offsetSign(first)<0`, the range is decreasing.
A range for a recursive enumerable type is always
increasing.
The _span operator_ `..` is an abbreviation for `span()`:
for (i in min..max) { ... }
if (char in 'A'..'Z') { ... }
The span operator accepts the first and last values of
the range. It may produce an increasing range:
0..5 // [0, 1, 2, 3, 4, 5]
0..0 // [0]
Or it may produce a decreasing range:
5..0 // [5, 4, 3, 2, 1, 0]
0..-5 // [0, -1, -2, -3, -4, -5]"
shared Range span
(Element first, Element last)
given Element satisfies Enumerable
=> Span(first, last);
© 2015 - 2024 Weber Informatics LLC | Privacy Policy