Ranges¶
A range is a first-class, constant-space representation of a regular sequence of values. These values are typically integers, though ranges over bools, enums, and other types are also supported. Ranges support serial and parallel iteration over the sequence of values they represent, as well as operations such as counting, striding, intersection, shifting, and comparisons. Ranges form the basis for defining rectangular domains (Domains) and arrays (Arrays) in Chapel.
Ranges are presented as follows:
definition of the key range concepts Range Concepts
range types Range Types
range values Range Values
range assignment Range Assignment
operators on ranges Range Operators
predefined functions on ranges Predefined Routines on Ranges
Range Concepts¶
A range has four primary properties. Together they define the sequence of indices that the range represents, or the represented sequence, as follows.
The low bound is either a specific index value or -\(\infty\).
The high bound is either a specific index value or +\(\infty\). The low and high bounds determine the span of the represented sequence. Chapel does not represent \(\infty\) explicitly. Instead, infinite bound(s) are represented implicitly in the range’s type (Range Types). When the low and/or high bound is \(\infty\), the represented sequence is unbounded in the corresponding direction(s).
The stride is a non-zero integer. It defines the distance between any two adjacent members of the represented sequence. The sign of the stride indicates the direction of the sequence:
\(stride > 0\) indicates an increasing sequence,
\(stride < 0\) indicates a decreasing sequence.
The alignment is either a specific index value or is ambiguous. It defines how the represented sequence’s members are aligned relative to the stride. For a range with a stride other than 1 or -1, ambiguous alignment means that the represented sequence is undefined. In such a case, certain operations discussed later result in an error.
More formally, the represented sequence for the range \((low, high, stride, alignmt)\) contains all indices \(ix\) such that:
\(low \leq ix \leq high\) |
if \(stride = 1\) or \(stride = -1\) |
\(low \leq ix \leq high\) and \(ix \equiv alignmt \pmod{|stride|}\) |
if \(alignmt\) is not ambiguous |
the represented sequence is undefined |
otherwise |
The sequence, if defined, is increasing if \(stride > 0\) and decreasing if \(stride < 0\).
If the represented sequence is defined but there are no indices satisfying the applicable equation(s) above, the range and its represented sequence are empty. A common case of this occurs when the high bound is greater than the low bound.
We say that a value \(ix\) is aligned w.r.t. the range \((low, high, stride, alignmt)\) if:
\(alignmt\) is not ambiguous and \(ix \equiv alignmt \pmod{|stride|}\), or
\(stride\) is 1 or -1.
Furthermore, \(\infty\) is never aligned.
Ranges have the following additional properties.
A range is ambiguously aligned if
its alignment is ambiguous, and
its stride is neither 1 nor -1.
The first index is the first member of the represented sequence.
A range has no first index when the first member is undefined, that is, in the following cases:
the range is ambiguously aligned,
the represented sequence is empty,
the represented sequence is increasing and the low bound is -\(\infty\),
the represented sequence is decreasing and the high bound is +\(\infty\).
The last index is the last member of the represented sequence.
A range has no last index when the last member is undefined, that is, in the following cases:
it is ambiguously aligned,
the represented sequence is empty,
the represented sequence is increasing and the high bound is +\(\infty\),
the represented sequence is decreasing and the low bound is -\(\infty\).
The aligned low bound is the smallest value that is greater than or equal to the low bound and is aligned w.r.t. the range, if such a value exists.
The aligned low bound equals the smallest member of the represented sequence, when both exist.
The aligned high bound is the largest value that is less than or equal to the high bound and is aligned w.r.t. the range, if such a value exists.
The aligned high bound equals the largest member of the represented sequence, when both exist.
The range is iterable, that is, it is legal to iterate over it, if it has a first index.
Range Types¶
The type of a range is characterized by three properties:
idxType
is the type of the values in the range’s represented sequence. However, when the range’s low and/or high bound is \(\infty\), the represented sequence also contains indices that are not representable byidxType
.idxType
must be an integral, boolean, or enumerated type and isint
by default. The range’s low bound and high bound (when they are not \(\infty\)) and alignment are of the typeidxType
. The range’s stride is of the signed integer type that has the same bit size asidxType
for integral ranges; for boolean and enumerated ranges, it is simplyint
.boundedType
indicates which of the range’s bounds are not \(\infty\).boundedType
is an enumeration constant of the typeBoundedRangeType
. It is discussed further below.stridable
is a boolean that determines whether the range’s stride can take on values other than 1.stridable
isfalse
by default. A range is called stridable if its type’sstridable
field istrue
.
boundedType
is one of the constants of the following enumeration:
- enum BoundedRangeType { bounded, boundedLow, boundedHigh, boundedNone };¶
The value of boundedType
determines which bounds of the range are
specified (making the range “bounded”, as opposed to infinite, in the
corresponding direction(s)) as follows:
bounded
: both bounds are specified.boundedLow
: the low bound is specified (the high bound is +\(\infty\)).boundedHigh
: the high bound is specified (the low bound is -\(\infty\)).boundedNone
: neither bound is specified (both bounds are \(\infty\)).
boundedType
is BoundedRangeType.bounded
by default.
The parameters idxType
, boundedType
, and stridable
affect
all values of the corresponding range type. For example, the range’s low
bound is -\(\infty\) if and only if the boundedType
of that
range’s type is either boundedHigh
or boundedNone
.
Rationale.
Providing
boundedType
andstridable
in a range’s type allows the compiler to identify and optimize the common cases where the range isbounded
and/or its stride is 1.
A range type has the following syntax:
range-type:
'range' ( named-expression-list )
That is, a range type is obtained as if by invoking the range type constructor (The Type Constructor) that has the following header:
proc range(type idxType = int,
param boundedType = BoundedRangeType.bounded,
param stridable = false) type
As a special case, the keyword range
without a parenthesized
argument list refers to the range type with the default values of all
its parameters, i.e., range(int, BoundedRangeType.bounded, false)
.
Example (rangeVariable.chpl).
The following declaration declares a variable
r
that can represent ranges of 32-bit integers, with both low and high bounds specified, and the ability to have a stride other than 1.var r: range(int(32), BoundedRangeType.bounded, stridable=true);
Range Values¶
A range value consists of the range’s four primary properties (Range Concepts): low bound, high bound, stride and alignment.
Range Literals¶
Range literals are specified with the following syntax.
range-literal:
expression .. expression
expression ..< expression
expression ..
.. expression
..< expression
..
The expressions to the left and to the right of ..
or ..<
,
when given, are called the lower bound expression and the upper
bound expression, respectively. The ..
operator defines a
closed-interval range, whereas the ..<
operator defines a
half-open interval.
The type of a range literal is a range with the following parameters:
idxType
is determined as follows:If both the lower bound and the upper bound expressions are given and have the same type, then
idxType
is that type.If both the lower bound and the upper bound expressions are given and an implicit conversion is allowed from one expression’s type to the other’s, then
idxType
is that type.If only one bound expression is given and it has an integral, boolean, or enumerated type, then
idxType
is that type.If neither bound expression is given, then
idxType
isint
.Otherwise, the range literal is not legal.
boundedType
is a value of the typeBoundedRangeType
that is determined as follows:bounded
, if both the lower bound and the upper bound expressions are given,boundedLow
, if only the upper bound expression is given,boundedHigh
, if only the lower bound expression is given,boundedNone
, if neither bound expression is given.
stridable
isfalse
.
The value of a range literal is as follows:
The low bound is given by the lower bound expression, if present, and is -\(\infty\) otherwise.
When the range has an upper bound expression, a closed-interval range (
..
) takes the expression’s value as its high bound; whereas the high bound of a half-open interval range (..<
) excludes the upper bound and is one less than the upper bound expression. If there is no upper bound expression, the high bound is +\(\infty\).The stride is 1.
The alignment is ambiguous.
Default Values¶
The default value for a range with an integral idxType
depends on
the type’s boundedType
parameter as follows:
1..0
(an empty range) ifboundedType
isbounded
1..
ifboundedType
isboundedLow
..0
ifboundedType
isboundedHigh
..
ifboundedType
isboundedNone
Rationale.
We use 0 and 1 to represent an empty range because these values are available for any integer
idxType
with more than one value.We have not found the natural choice of the default value for
boundedLow
andboundedHigh
ranges. The values indicated above are distinguished by the following property. Slicing the default value for aboundedLow
range with the default value for aboundedHigh
range (or visa versa) produces an empty range, matching the default value for abounded
range
Default values of ranges with boolean idxType
are similar, but
substituting false
and true
for 0 and 1 above. Ranges with
enum
idxType
use the 0th and 1st values in the enumeration in
place of 0 and 1 above. If the enum only has a single value, the
default value uses the 0th value as the low bound and has an undefined
high bound; the .size
query should be used with such ranges before
querying the high bound to determine whether or not it is valid.
Common Operations¶
All operations on a range return a new range rather than modifying the existing one. This supports a coding style in which all range values are immutable.
Rationale.
The intention is to provide ranges as immutable objects.
Immutable objects may be cached without creating coherence concerns. They are also inherently thread-safe. In terms of implementation, immutable objects are created in a consistent state and stay that way: Outside of initializers, internal consistency checks can be dispensed with.
These are the same arguments as were used to justify making strings immutable in Java and C#.
Range Assignment¶
Assigning one range to another results in the target range copying the low and high bounds, stride, and alignment from the source range.
Range assignment is legal when:
An implicit conversion is allowed from
idxType
of the source range toidxType
of the destination range type,the two range types have the same
boundedType
, andeither the destination range is stridable or the source range is not stridable.
Range Comparisons¶
Ranges can be compared using equality and inequality.
- operator ==(r1: range(?), r2: range(?)): bool¶
Returns
true
if the two ranges have the same represented sequence or the same four primary properties, andfalse
otherwise.
- operator !=(r1: range(?), r2: range(?)): bool¶
Returns
false
if the two ranges have the same represented sequence or the same four primary properties, andtrue
otherwise.
Iterating over Ranges¶
A range can be used as an iterator expression in a loop. This is legal only if the range is iterable. In this case, the loop iterates over the members of the range’s represented sequence in the order defined by the sequence. If the range is empty, no iterations are executed.
Overflow of the index variable while iterating over an unbounded range leads to undefined behavior. For unbounded ranges of bool or enum index type, the iteration will stop at the last value represented by the type.
In order for it to be possible to iterate over a range with a last index, it needs to be possible to add the stride to the range’s last index without overflowing the index type. In other words, the last index plus the stride must be between the index type’s minimum and maximum value (inclusive). If this property is not met, the program will have undefined behavior.
Implementation Notes.
When bounds checking is enabled, the case in the above paragraph is checked at runtime and the program will halt if the range iteration is invalid.
Iterating over Unbounded Ranges¶
When an unbounded range of integer values is used to drive a loop,
either by being the only iterand, or by serving as the leader iterand
of a zippered iteration, it will generate a conceptually infinite
number of iterations. In order for such loops to be useful in
practice, they must typically contain a break
or return
statement. Of course, in practice, the values representable by
idxType
are finite; as a result, when the loop reaches its extreme
values, the behavior of the loop is undefined.
Implementation Notes.
In the current implementation of Chapel, the loop will halt with an error once it yields a value within
stride
of the maximalidxType
value.
When an unbounded range of integer values serves as a follower iterand in a zippered context ( Zippered Iteration), it will generate as many indices as are needed to match its leader iterand.
Example (zipWithUnbounded.chpl).
The code
for i in zip(1..5, 3..) do write(i, "; ");produces the output
(1, 3); (2, 4); (3, 5); (4, 6); (5, 7);
When an unbounded range of bool
or enum
values is used in a
loop context, it is equivalent to a bounded range where the omitted
low/high bound is taken to be the false
/true
for a bool
range or the type’s initial/final value for an enum
range.
Range Promotion of Scalar Functions¶
Range values may be passed to a scalar function argument whose type matches the range’s index type. This results in a promotion of the scalar function as described in Promotion.
Example (rangePromotion.chpl).
Given a function
addOne(x: int)
that acceptsint
values, the functionaddOne()
can be called with the range1..10
as its actual argument, which will result in the function being invoked for each value in the range in a data-parallel manner.proc addOne(x: int) { return x + 1; } var A: [1..10] int; A = addOne(1..10);The last statement is equivalent to:
forall (a, i) in zip(A, 1..10) do a = addOne(i);
Range Operators¶
The following operators can be applied to range expressions and are
described in this section: stride (by
), alignment (align
), count
(#
) and slicing (()
or []
). Chapel also defines a set
of functions that operate on ranges. They are described in
Predefined Routines on Ranges.
range-expression:
expression
strided-range-expression
counted-range-expression
aligned-range-expression
sliced-range-expression
By Operator¶
The by
operator selects a subsequence of the range’s represented
sequence, optionally reversing its direction. The operator takes two
arguments, a base range and an integral step. It produces a new range
whose represented sequence contains each \(|\)step\(|\)-th
element of the base range’s represented sequence. The operator reverses
the direction of the represented sequence if step\(<\)0. If the
resulting sequence is increasing, it starts at the base range’s aligned
low bound, if it exists. If the resulting sequence is decreasing, it
starts at the base range’s aligned high bound, if it exists. Otherwise,
the base range’s alignment is used to determine which members of the
represented sequence to retain. If the base range’s represented sequence
is undefined, the resulting sequence is undefined, too.
The syntax of the by
operator is:
strided-range-expression:
range-expression 'by' step-expression
step-expression:
expression
The step expression can be any integral or boolean type. Boolean values are interpreted as 0 or 1, respectively. It is an error for the step to be zero.
The type of the result of the by
operator is the type of the base
range, but with the stridable
parameter set to true
.
Formally, the result of the by
operator is a range with the
following primary properties:
The low and upper bounds are the same as those of the base range.
The stride is the product of the base range’s stride and the step, cast to the base range’s stride type before multiplying.
The alignment is:
the aligned low bound of the base range, if such exists and the stride is positive;
the aligned high bound of the base range, if such exists and the stride is negative;
the same as that of the base range, otherwise.
Example (rangeByOperator.chpl).
In the following declarations, range
r1
represents the odd integers between 1 and 20. Ranger2
stridesr1
by two and represents every other odd integer between 1 and 20: 1, 5, 9, 13 and 17.var r1 = 1..20 by 2; var r2 = r1 by 2;Rationale.
Why isn’t the high bound specified first if the stride is negative? The reason for this choice is that the
by
operator is binary, not ternary. Given a rangeR
initialized to1..3
, we wantR by -1
to contain the ordered sequence \(3,2,1\). But thenR by -1
would be different from3..1 by -1
even though it should be identical by substituting the value in R into the expression. We have also found that using a strict ordering of low and high bounds can be clearer when using ranges to slice arrays.
Align Operator¶
The align
operator can be applied to any range, and creates a new
range with the given alignment.
The syntax for the align
operator is:
aligned-range-expression:
range-expression 'align' expression
The type of the resulting range expression is the same as that of the
range appearing as the left operand, but with the stridable
parameter set to true
. An implicit conversion from the type of the
right operand to the index type of the operand range must be allowed.
The resulting range has the same low and high bounds and stride as the
source range. The alignment equals the align
operator’s right
operand and therefore is not ambiguous.
Example (alignedStride.chpl).
var r1 = 0 .. 10 by 3 align 0; for i in r1 do write(" ", i); // Produces " 0 3 6 9". writeln();var r2 = 0 .. 10 by 3 align 1; for i in r2 do write(" ", i); // Produces " 1 4 7 10". writeln();
When the stride is negative, the same indices are printed in reverse:
Example (alignedNegStride.chpl).
var r3 = 0 .. 10 by -3 align 0; for i in r3 do write(" ", i); // Produces " 9 6 3 0". writeln();var r4 = 0 .. 10 by -3 align 1; for i in r4 do write(" ", i); // Produces " 10 7 4 1". writeln();
To create a range aligned relative to its first
index, see
range.offset
below.
Count Operator¶
The #
operator takes a range and an integral count and creates a
new range containing the specified number of indices. The low or high
bound of the left operand is preserved, and the other bound adjusted to
provide the specified number of indices. If the count is positive,
indices are taken from the start of the range; if the count is negative,
indices are taken from the end of the range.
counted-range-expression:
range-expression # expression
The count expression can be any integral or boolean value, where
boolean values are interpreted as 0 or 1, respectively. The count
value must be less than or equal to the size
of the range.
The type of the result of the #
operator is the type of the range
argument.
Depending on the sign of the count and the stride, the high or low bound is unchanged and the other bound is adjusted so that it is \(c * stride - 1\) units away. Specifically:
If the count times the stride is positive, the low bound is preserved and the high bound is adjusted to be one less than the low bound plus that product.
If the count times the stride is negative, the high bound is preserved and the low bound is adjusted to be one greater than the high bound plus that product.
Rationale.
Following the principle of preserving as much information from the original range as possible, we must still choose the other bound so that exactly count indices lie within the range. Making the two bounds lie \(count * stride - 1\) apart will achieve this, regardless of the current alignment of the range.
This choice also has the nice symmetry that the alignment can be adjusted without knowing the bounds of the original range, and the same number of indices will be produced:
r # 4 align 0 // Contains four indices. r # 4 align 1 // Contains four indices. r # 4 align 2 // Contains four indices. r # 4 align 3 // Contains four indices.
It is an error to apply the count operator with a positive count to a range that has no first index. It is also an error to apply the count operator with a negative count to a range that has no last index. It is an error to apply the count operator to a range that is ambiguously aligned.
Example (rangeCountOperator.chpl).
The following declarations result in equivalent ranges.
var r1 = 1..10 by -2 # -3; var r2 = ..6 by -2 # 3; var r3 = -6..6 by -2 # 3; var r4 = 1..#6 by -2;Each of these ranges represents the ordered set of three indices: 6, 4, 2.
Arithmetic Operators¶
The following arithmetic operators are defined on ranges and integral types:
proc +(r: range, s: integral): range
proc +(s: integral, r: range): range
proc -(r: range, s: integral): range
The +
and -
operators apply the scalar via the operator to the
range’s low and high bounds, producing a shifted version of the range.
If the operand range is unbounded above or below, the missing bounds are
ignored. The index type of the resulting range is the type of the value
that would result from an addition between the scalar value and a value
with the range’s index type. The bounded and stridable parameters for
the result range are the same as for the input range.
The stride of the resulting range is the same as the stride of the original. The alignment of the resulting range is shifted by the same amount as the high and low bounds. It is permissible to apply the shift operators to a range that is ambiguously aligned. In that case, the resulting range is also ambiguously aligned.
Example (rangeAdd.chpl).
The following code creates a bounded, non-stridable range
r
which has an index type ofint
representing the indices \({0, 1, 2, 3}\). It then uses the+
operator to create a second ranger2
representing the indices \({1, 2, 3, 4}\). Ther2
range is bounded, non-stridable, and is represented by indices of typeint
.var r = 0..3; var r2 = r + 1; // 1..4
Range Slicing¶
Ranges can be sliced using other ranges to create new sub-ranges. The
resulting range represents the intersection between the two ranges’
represented sequences. The stride and alignment of the resulting range
are adjusted as needed to make this true. idxType
and the sign of
the stride of the result are determined by the first operand.
Range slicing is specified by the syntax:
sliced-range-expression:
range-expression ( range-expression )
range-expression [ range-expression ]
If either of the operand ranges is ambiguously aligned, then the resulting range is also ambiguously aligned. In this case, the result is valid only if the strides of the operand ranges are relatively prime. Otherwise, an error is generated at run time.
Rationale.
If the strides of the two operand ranges are relatively prime, then they are guaranteed to have some elements in their intersection, regardless whether their relative alignment can be determined. In that case, the bounds and stride in the resulting range are valid with respect to the given inputs. The alignment can be supplied later to create a valid range.
If the strides are not relatively prime, then the result of the slicing operation would be completely ambiguous. The only reasonable action for the implementation is to generate an error.
If the resulting sequence cannot be expressed as a range of the original
type, the slice expression evaluates to the empty range 1..0
. This
can happen, for example, when the operands represent all odd and all
even numbers, or when the first operand is an unbounded range with
unsigned idxType
and the second operand represents only negative
numbers.
Example (rangeSlicing.chpl).
In the following example,
r
represents the integers from 1 to 20 inclusive. Rangesr2
andr3
are defined using range slices and represent the indices from 3 to 20 and the odd integers between 1 and 20 respectively. Ranger4
represents the odd integers between 1 and 20 that are also divisible by 3.var r = 1..20; var r2 = r[3..]; var r3 = r[1.. by 2]; var r4 = r3[0.. by 3];
Predefined Routines on Ranges¶
Range Type Queries¶
- proc range.boundedType param : BoundedRangeType¶
Returns which bounds the range explicitly represents (its
boundedType
value).
- proc range.idxType type¶
Returns the type of the range’s indices (its
idxType
).
- proc range.stridable param : bool¶
Returns whether or not the range can have non-unit strides (its
stridable
value).
- proc range.stride¶
Returns the range’s stride.
- proc range.alignment¶
Returns the range’s alignment.
- proc range.aligned¶
Returns
true
if the range’s alignment is unambiguous,false
otherwise.
- proc isBoundedRange(r: range(?)) param¶
Returns
true
if argumentr
is a fully bounded range,false
otherwise.
- proc range.isBounded() param¶
Returns
true
if this range is bounded,false
otherwise.
- config param alignedBoundsByDefault = true¶
This used to control whether or not the
range.low
/range.high
queries returned aligned values by default.Warning
alignedBoundsByDefault
is deprecated and no longer has any effect
- proc range.hasLowBound() param¶
Returns
true
if this range’s low bound is not -\(\infty\), andfalse
otherwise.
- proc range.lowBound: idxType¶
Returns the range’s low bound. If the range does not have a low bound (e.g.,
..10
), the behavior is undefined. See alsorange.hasLowBound
.
- proc range.low: idxType¶
Returns the range’s aligned low bound. If this bound is undefined (e.g.,
..10 by -2
), the behavior is undefined.Example:
var r = 1..10 by -2; writeln(r.low);
produces the output
2
- proc range.alignedLow: idxType¶
Return the range’s aligned low bound. Note that this is a synonym for
range.low
.Warning
‘.alignedLow’ is deprecated; please use ‘.low’ instead
- proc range.hasHighBound() param¶
Returns
true
if this range’s high bound is not \(\infty\), andfalse
otherwise.
- proc range.highBound: idxType¶
Return the range’s high bound. If the range does not have a high bound (e.g.,
1..
), the behavior is undefined. See alsorange.hasHighBound
.
- proc range.high: idxType¶
Returns the range’s aligned high bound. If the aligned high bound is undefined (e.g.,
1.. by 2
), the behavior is undefined.Example:
var r = 1..10 by 2; writeln(r.high);
produces the output
9
- proc range.alignedHigh: idxType¶
Return the range’s aligned high bound. Note that this is a synonym for
range.high
.Warning
‘.alignedHigh’ is deprecated; please use ‘.high’ instead
- proc range.isNaturallyAligned()¶
Returns
true
if this range is naturally aligned,false
otherwise.
- proc range.isAmbiguous() param¶
Returns
true
if the range is ambiguously aligned,false
otherwise.
- proc range.isEmpty()¶
Returns
true
if the sequence represented by the range is empty andfalse
otherwise. If the range is ambiguous, the behavior is undefined.
- proc range.size: int¶
Returns the number of values represented by this range as an integer.
If the size exceeds
max(int)
, this procedure will halt when bounds checks are on and have undefined behavior when they are not.If the represented sequence is infinite or undefined, an error is generated.
- proc range.sizeAs(type t: integral): t¶
Returns the number of elements in this range as the specified integer type.
If the size exceeds the maximal value of that type, this procedure will halt when bounds checks are on and have undefined behavior when they are not.
If the represented sequence is infinite or undefined, an error is generated.
- proc range.hasFirst() param¶
Returns
true
if the range has a first index,false
otherwise. Note that in the event that the range is stridable and at least partially bounded, the return value will not be aparam
.
- proc range.first¶
Returns the first value in the sequence the range represents. If the range has no first index, the behavior is undefined. See also
range.hasFirst
.
- proc range.hasLast() param¶
Returns
true
if the range has a last index,false
otherwise. Note that in the event that the range is stridable and at least partially bounded, the return value will not be aparam
.
- proc range.last¶
Returns the last value in the sequence the range represents. If the range has no last index, the behavior is undefined. See also
range.hasLast
.
- proc range.contains(ind: idxType)¶
Returns
true
if the range’s represented sequence containsind
,false
otherwise. It is an error to invokecontains
if the represented sequence is not defined.
- proc range.contains(other: range(?))
Returns
true
if the rangeother
is contained within this one,false
otherwise.
- proc range.boundsCheck(other: range(?e, ?b, ?s))¶
Returns
true
ifother
lies entirely within this range andfalse
otherwise. Returnsfalse
if either range is ambiguously aligned.
- proc range.boundsCheck(other: idxType)
Returns
true
ifother
is contained in this range andfalse
otherwise.
- proc range.indexOrder(ind: idxType)¶
Returns an integer representing the zero-based ordinal value of
ind
within the range’s sequence of values if it is a member of the sequence. Otherwise, returns -1. It is an error to invokeindexOrder
if the represented sequence is not defined or the range does not have a first index.The following calls show the order of index 4 in each of the given ranges:
(0..10).indexOrder(4) == 4 (1..10).indexOrder(4) == 3 (3..5).indexOrder(4) == 1 (0..10 by 2).indexOrder(4) == 2 (3..5 by 2).indexOrder(4) == -1
- proc range.orderToIndex(ord: integral): idxType¶
Returns the zero-based
ord
-th element of this range’s represented sequence. It is an error to invokeorderToIndex
if the range is not defined, or iford
is negative or greater than the range’s size. TheorderToIndex
procedure is the reverse ofindexOrder
.Example:
0..10.orderToIndex(4) == 4 1..10.orderToIndex(3) == 4 3..5.orderToIndex(1) == 4 0..10 by 2.orderToIndex(2) == 4
- proc range.translate(offset: integral)¶
Returns a range with elements shifted from this range by
offset
. Formally, the range’s low bound, high bound, and alignment values will be shifted while the stride value will be preserved. If the range’s alignment is ambiguous, the behavior is undefined.Example:
0..9.translate(1) == 1..10 0..9.translate(2) == 2..11 0..9.translate(-1) == -1..8 0..9.translate(-2) == -2..7
- proc range.expand(offset: integral)¶
Returns a range expanded by
offset
elements from each end. Ifoffset
is negative, the range will be contracted. The stride and alignment of the original range are preserved.Example:
0..9.expand(1) == -1..10 0..9.expand(2) == -2..11 0..9.expand(-1) == 1..8 0..9.expand(-2) == 2..7
Formally, for a range represented by the tuple \((l,h,s,a)\), the result is \((l-i,h+i,s,a)\). If the operand range is ambiguously aligned, then so is the resulting range.
- proc range.interior(offset: integral)¶
Returns a range with
offset
elements from the interior portion of this range. Ifoffset
is positive, take elements from the high end, and ifoffset
is negative, take elements from the low end.Example:
0..9.interior(1) == 9..9 0..9.interior(2) == 8..9 0..9.interior(-1) == 0..0 0..9.interior(-2) == 0..1
Formally, given a range denoted by the tuple \((l,h,s,a)\),
if \(i < 0\), the result is \((l,l-(i-1),s,a)\),
if \(i > 0\), the result is \((h-(i-1),h,s,a)\), and
if \(i = 0\), the result is \((l,h,s,a)\).
This differs from the behavior of the count operator, in that
interior()
preserves the alignment, and it uses the low and high bounds rather thanfirst
andlast
to establish the bounds of the resulting range. If the operand range is ambiguously aligned, then so is the resulting range.
- proc range.exterior(offset: integral)¶
Return a range with
offset
elements from the exterior portion of this range. Ifoffset
is positive, take elements from the high end, and ifoffset
is negative, take elements from the low end.Example:
0..9.exterior(1) = 10..10 0..9.exterior(2) = 10..11 0..9.exterior(-1) = -1..-1 0..9.exterior(-2) = -2..-1
Formally, given a range denoted by the tuple \((l,h,s,a)\),
if \(i < 0\), the result is \((l+i,l-1,s,a)\),
if \(i > 0\), the result is \((h+1,h+i,s,a)\), and
if \(i = 0\), the result is \((l,h,s,a)\).
If the operand range is ambiguously aligned, then so is the resulting range.
- proc range.offset(in offset: integral)¶
Returns a range whose alignment is this range’s first index plus
offset
. If the range has no first index, a runtime error is generated.