Ranges¶
A range
is a first-class, constant-space representation of a
regular sequence of values. These values are typically integers,
though ranges over enum types are also supported. Ranges
support iteration over the sequences they represent as well as
operations such as counting, striding, intersection, shifting, and
comparisons.
Range Values¶
In their simplest form, ranges are represented by their low and high bounds:
1..3 // 1, 2, 3
0..n // 0, 1, 2, 3, ..., n
lo..hi // lo, lo+1, lo+2, ..., hi
Ranges may also be unbounded, in which case, the lower and/or upper bounds may be omitted:
1.. // 1, 2, 3, ...
..10 // .., 8, 9, 10
.. // ..., -2, -1, 0, 1, 2, ...
Ranges over enum types respect the declaration order of its values:
enum color {red=4, orange=2, yellow=1, green=3, blue=6, indigo=7, violet=5};
color.orange..color.green; // orange, yellow, green
Range Types¶
Range types are generic with respect to three fields:
idxType
: The type of the range’s values—must an integral or enum type (defaults toint
)boundedType
: ABoundedRangeType
value indicating which bounds the range stores (defaults tobounded
)stridable
: A boolean indicating whether or not the range can be strided (defaults tofalse
)
The following code shows range variables declared with specified type signatures:
var r1: range = 1..10;
var r2: range(int(8)) = 1..myInt8;
var r3: range(color) = color.green..color.blue;
var r4: range(stridable=true) = 1..10 by 2;
var r5: range(boundedType=BoundedRangeType.boundedNone) = ..;
Like other variables, these types can be inferred by the compiler from the initializing expressions for simplicity:
var r1 = 1..10;
var r2 = 1..myInt8;
var r3 = color.green..color.blue;
var r4 = 1..10 by 2;
var r5 = ..;
Range Operators¶
New ranges can be constructed from existing ones using the counting,
striding, and/or alignment operators, #
, by
, and align
:
0..#10 // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
0..10 by 2 // 0, 2, 4, 6, 8, 10
0..10 by 2 align 1 // 1, 3, 5, 7, 9
0.. by 2 # 10 // 0, 2, 4, 6, 8, 10, 12, 14, 16, 18
Iteration over ranges¶
Ranges can be used as the iterable expression in for
, forall
, and coforall
loops.
for i in 1..10 { ... f(i) ... }
forall i in 1..1000 { ... f(i) ... }
coforall i in 0..#numTasks { ... f(i) ... }
When ranges that are not fully bounded are zipped with another iterator, the other iterator is used to determine an ending point.
// (i, j) will take the values: (1, 7), (2, 8), (3, 9), (4, 10)
for (i, j) in zip(1..4, 7..) { ... }
// (i, j) will take the values: (1, 10), (2, 9), (3, 8), (4, 7)
for (i,j) in zip(1..4, ..10 by -1) { ... }
Range Intersection¶
A range can be intersected with another range to form a new range representing the intersection of the two ranges by slicing one range with the other.
(1..10)[3..8] // 3..8
(0..20)[1..20 by 2] // 1..20 by 2
(1..10)[5..] // 5..10
(1..10)[..5] // 1..5
Range Shifting¶
A range can be shifted by an integer using the +
and -
operators.
(1..10) + 5 // 6..15
(1..10) - 3 // -2..7
(1..) + 1 // 2..
(..10) + 1 // ..11
Range Comparisons¶
Ranges can be compared for equality using the ==
and !=
operators.
1..10 == 1..10 // true
1.. == 1.. // true
1..10 != (1..10 by 2) // true
- config param sizeReturnsInt = false¶
Chapel is in the process of changing
range(idxType).size
away from returning the range’s size as anidxType
value in favor of returning anint
value. SettingsizeReturnsInt
totrue
permits a user to opt into this new behavior now rather than having it change out from under them in a future release. The old behavior can be retained by using the newrange.sizeAs
method.
- enum BoundedRangeType { bounded, boundedLow, boundedHigh, boundedNone }¶
The
BoundedRangeType
enum is used to specify the types of bounds a range is required to have.bounded
- The range has finite low and high bounds.boundedLow
- The range starts at a given low bound, but conceptually goes up to infinity.boundedHigh
- The range conceptually starts at negative infinity and ends at a given high bound.boundedNone
- The range conceptually runs from negative infinity to infinity.
- proc range.intIdxType type¶
The
idxType
as represented by an integer type. WhenidxType
is an enum type, this evaluates toint
. Otherwise, it evaluates toidxType
.
- proc isRangeType(type t) param¶
Return true if argument
t
is a range type, false otherwise
- proc isBoundedRange(r: range(?)) param¶
Return true if argument
r
is a fully bounded range, false otherwise
- proc range.isBounded() param¶
Return true if this range is bounded
- proc range.hasLowBound() param¶
Return true if this range has a low bound, false otherwise
- proc range.hasHighBound() param¶
Returns true if this range has a high bound, false otherwise
- proc range.stride¶
Returns the stride of the range
- proc range.alignment¶
Returns the alignment of the range
- proc range.aligned¶
Returns true if the range is aligned
- proc range.first¶
Return the first element in the sequence the range represents
- proc range.last¶
Return the last element in the sequence the range represents
- proc range.low¶
Return the range’s low bound. If the range does not have a low bound (e.g.,
..10
), a compiler error is generated.
- proc range.high¶
Return the range’s high bound. If the range does not have a high bound (e.g.,
1..
), a compiler error is generated.
- proc range.alignedLow: idxType¶
Returns the range’s aligned low bound. If the aligned low bound is undefined (e.g.,
..10 by -2
), a compiler error is generated.
- proc range.alignedHigh: idxType¶
Returns the range’s aligned high bound. If the aligned high bound is undefined (e.g.,
1.. by 2
), a compiler error is generated.
- proc range.isEmpty()¶
If the sequence represented by the range is empty, return true. An error is reported if the range is ambiguous.
- proc range.size¶
Returns the number of elements in this range as an integer. Historically, and by default for now, the return type is represented as an
intIdxType
value. However, Chapel is in the process of changing to always return anint
value, and so will generate a warning ifintIdxType != int
to alert users to the change.sizeReturnsInt
can be used to opt into the new behavior now. Orrange.sizeAs
can be used to request a different return type.If the size exceeds
max(intIdxType)
/max(int)
, this procedure will halt when bounds checks are on.
- 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.
- proc range.hasFirst() param¶
Return true if the range has a first index, false otherwise
- proc range.hasLast() param¶
Return true if the range has a last index, false otherwise
- 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.contains(ind: idxType)¶
Returns true if
ind
is in this range, false otherwise
- proc range.contains(other: range(?))
Returns true if the range
other
is contained within this one, false otherwise
- proc ident(r1: range(?), r2: r1.type)¶
Warning
This procedure is deprecated - please let us know if you were relying on it.
- Returns true if the two ranges are the same in every respect: i.e. the
two ranges have the same
idxType
,boundedType
,stridable
,low
,high
,stride
andalignment
values.
- proc range.boundsCheck(other: range(?e, ?b, ?s))¶
Returns true if
other
lies entirely within this range and false otherwise. Returns false if either range is ambiguously aligned.
- proc range.boundsCheck(other: idxType)
Return true if
other
is contained in this range and false otherwise
- proc range.indexOrder(ind: idxType)¶
If
ind
is a member of the range’s represented sequence, returns an integer giving the ordinal index of ind within the sequence using zero-based indexing. Otherwise, returns(-1):
range.intIdxType
. 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)¶
Return a range with elements shifted from this range by
offset
.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.translate(offset: integral)
- proc range.interior(offset: integral)¶
Return 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
- 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
- proc range.expand(offset: integral)¶
Return a range expanded by
offset
elements from each end. Ifoffset
is negative, the range will be contracted.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
- 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.