class Range is Cool does Iterable does Positional {}
Ranges serve two main purposes: to generate lists of consecutive numbers or strings, and to act as a matcher to check if a number or string is within a certain range.
Ranges are constructed using one of the four possible range operators, which consist of two dots, and optionally a caret which indicates that the endpoint marked with it is excluded from the range.
1 .. 5; # 1 <= $x <= 5 1^.. 5; # 1 < $x <= 5 1 ..^5; # 1 <= $x < 5 1^..^5; # 1 < $x < 5
The caret is also a prefix operator for constructing numeric ranges starting from zero:
my $x = 10; say ^$x; # same as 0 ..^ $x.Numeric
Iterating a range (or calling the list
method) uses the same semantics as the ++
prefix and postfix operators, i.e., it calls the succ
method on the start point, and then the generated elements.
Ranges always go from small to larger elements; if the start point is bigger than the end point, the range is considered empty.
for 1..5 { .say }; # OUTPUT: «12345» say ('a' ^..^ 'f').list; # OUTPUT: «(b c d e)» say 5 ~~ ^5; # OUTPUT: «False» say 4.5 ~~ 0..^5; # OUTPUT: «True» say (1.1..5).list; # OUTPUT: «(1.1 2.1 3.1 4.1)»
Use the ...
sequence operator to produce lists of elements that go from larger to smaller values, or to use offsets other than increment-by-1 and other complex cases.
Use ∞
or *
(Whatever) to indicate an end point to be open-ended.
for 1..* { .say }; # start from 1, continue until stopped for 1..∞ { .say }; # the same
Beware that a WhateverCode
end point, instead of a plain Whatever, will go through the range operator and create another WhateverCode which returns a Range:
# A Whatever produces the 1..Inf range say (1..*).^name; # OUTPUT: «Range» say (1..*); # OUTPUT: «1..Inf» # Upper end point is now a WhateverCode say (1..*+20).^name; # OUTPUT: «{ ... }» say (1..*+20).WHAT; # OUTPUT: «(WhateverCode)» say (1..*+20).(22); # OUTPUT: «1..42»
Ranges implement Positional
interface, so its elements can be accessed using an index. In a case when the index given is bigger than the Range object's size, Nil
object will be returned. The access works for lazy Range objects as well.
say (1..5)[1]; # OUTPUT: «2» say (1..5)[10]; # OUTPUT: «Nil» say (1..*)[10]; # OUTPUT: «11»
Ranges in subscripts§
A Range can be used in a subscript to get a range of values. Please note that assigning a Range to a scalar container turns the Range into an item. Use binding, @-sigiled containers or a slip to get what you mean.
my @numbers = <4 8 15 16 23 42>; my $range := 0..2; .say for @numbers[$range]; # OUTPUT: «4815» my @range = 0..2; .say for @numbers[@range]; # OUTPUT: «4815»
Shifting and scaling intervals§
It is possible to shift or scale the interval of a range:
say (1..10) + 1; # OUTPUT: «2..11» say (1..10) - 1; # OUTPUT: «0..9» say (1..10) * 2; # OUTPUT: «2..20» say (1..10) / 2; # OUTPUT: «0.5..5.0»
Matching against Ranges§
You can use smartmatch to match against Ranges.
say 3 ~~ 1..12; # OUTPUT: «True» say 2..3 ~~ 1..12; # OUTPUT: «True»
In Rakudo only, you can use the in-range
method for matching against a range, which in fact is equivalent to smartmatch except it will throw an exception when out of range, instead of returning False
:
say ('א'..'ת').in-range('ע'); # OUTPUT: «True»
However, if it is not included in the range:
say ('א'..'ת').in-range('p', "Letter 'p'"); # OUTPUT: «(exit code 1) Letter 'p' out of range. Is: "p", should be in "א".."ת"
The second parameter to in-range
is the optional message that will be printed with the exception. It will print Value
by default.
Methods§
method ACCEPTS§
multi method ACCEPTS(Range:D: Mu \topic) multi method ACCEPTS(Range:D: Range \topic) multi method ACCEPTS(Range:D: Cool:D \got) multi method ACCEPTS(Range:D: Complex:D \got)
Indicates if the Range
contains (overlaps with) another Range
. As an example:
my $p = Range.new( 3, 5 ); my $r = Range.new( 1, 10 ); say $p.ACCEPTS( $r ); # OUTPUT: «False» say $r.ACCEPTS( $p ); # OUTPUT: «True» say $r ~~ $p; # OUTPUT: «False» (same as $p.ACCEPTS( $r ) say $p ~~ $r; # OUTPUT: «True» (same as $r.ACCEPTS( $p )
An infinite Range
always contains any other Range
, therefore:
say 1..10 ~~ -∞..∞; # OUTPUT: «True» say 1..10 ~~ -∞^..^∞; # OUTPUT: «True»
Similarly, a Range
with open boundaries often includes other ranges:
say 1..2 ~~ *..10; # OUTPUT: «True» say 2..5 ~~ 1..*; # OUTPUT: «True»
It is also possible to use non-numeric ranges, for instance string based ones:
say 'a'..'j' ~~ 'b'..'c'; # OUTPUT: «False» say 'b'..'c' ~~ 'a'..'j'; # OUTPUT: «True» say 'raku' ~~ -∞^..^∞; # OUTPUT: «True» say 'raku' ~~ -∞..∞; # OUTPUT: «True» say 'raku' ~~ 1..*; # OUTPUT: «True»
When smartmatching a Range
of integers with a Cool
(string) the ACCEPTS
methods exploits the before and after operators in order to check that the Cool
value is overlapping the range:
say 1..10 ~~ '5'; # OUTPUT: «False» say '5' before 1; # OUTPUT: «False» say '5' after 10; # OUTPUT: «True» say '5' ~~ *..10; # OUTPUT: «False»
In the above example, since the '5'
string is after the 10
integer value, the Range
does not overlap with the specified value.
When matching with a Mu
instance (i.e., a generic instance), the cmp operator is used.
method min§
method min(Range:D:)
Returns the start point of the range.
say (1..5).min; # OUTPUT: «1» say (1^..^5).min; # OUTPUT: «1»
method excludes-min§
method excludes-min(Range:D: --> Bool:D)
Returns True
if the start point is excluded from the range, and False
otherwise.
say (1..5).excludes-min; # OUTPUT: «False» say (1^..^5).excludes-min; # OUTPUT: «True»
method max§
method max(Range:D:)
Returns the end point of the range.
say (1..5).max; # OUTPUT: «5» say (1^..^5).max; # OUTPUT: «5»
method excludes-max§
method excludes-max(Range:D: --> Bool:D)
Returns True
if the end point is excluded from the range, and False
otherwise.
say (1..5).excludes-max; # OUTPUT: «False» say (1^..^5).excludes-max; # OUTPUT: «True»
method bounds§
method bounds()
Returns a list consisting of the start and end point.
say (1..5).bounds; # OUTPUT: «(1 5)» say (1^..^5).bounds; # OUTPUT: «(1 5)»
method infinite§
method infinite(Range:D: --> Bool:D)
Returns True
if either end point was declared with ∞
or *
.
say (1..5).infinite; # OUTPUT: «False» say (1..*).infinite; # OUTPUT: «True»
method is-int§
method is-int(Range:D: --> Bool:D)
Returns True
if both end points are Int
values.
say ('a'..'d').is-int; # OUTPUT: «False» say (1..^5).is-int; # OUTPUT: «True» say (1.1..5.5).is-int; # OUTPUT: «False»
method int-bounds§
proto method int-bounds(|) multi method int-bounds() multi method int-bounds($from is rw, $to is rw --> Bool:D)
If the Range
is an integer range (as indicated by is-int), then this method returns a list with the first and last value it will iterate over (taking into account excludes-min and excludes-max). Returns a Failure
if it is not an integer range.
say (2..5).int-bounds; # OUTPUT: «(2 5)» say (2..^5).int-bounds; # OUTPUT: «(2 4)»
If called with (writable) arguments, these will take the values of the higher and lower bound and returns whether integer bounds could be determined from the Range
:
if (3..5).int-bounds( my $min, my $max) { say "$min, $max" ; # OUTPUT: «3, 5» } else { say "Could not determine integer bounds"; }
method minmax§
multi method minmax(Range:D: --> List:D)
If the Range
is an integer range (as indicated by is-int), then this method returns a list with the first and last value it will iterate over (taking into account excludes-min and excludes-max). If the range is not an integer range, the method will return a two element list containing the start and end point of the range unless either of excludes-min or excludes-max are True
in which case a Failure
is returned.
my $r1 = (1..5); my $r2 = (1^..5); say $r1.is-int, ', ', $r2.is-int; # OUTPUT: «True, True» say $r1.excludes-min, ', ', $r2.excludes-min; # OUTPUT: «False, True» say $r1.minmax, ', ', $r2.minmax; # OUTPUT: «(1 5), (2 5)» my $r3 = (1.1..5.2); my $r4 = (1.1..^5.2); say $r3.is-int, ', ', $r4.is-int; # OUTPUT: «False, False» say $r3.excludes-max, ', ', $r4.excludes-max; # OUTPUT: «False, True» say $r3.minmax; # OUTPUT: «(1.1 5.2)» say $r4.minmax; CATCH { default { put .^name, ': ', .Str } }; # OUTPUT: «X::AdHoc: Cannot return minmax on Range with excluded ends»
method elems§
method elems(Range:D: --> Numeric:D)
Returns the number of elements in the range, e.g. when being iterated over, or when used as a List
. Returns 0 if the start point is larger than the end point, including when the start point was specified as ∞
. Fails when the Range is lazy, including when the end point was specified as ∞
or either end point was specified as *
.
say (1..5).elems; # OUTPUT: «5» say (1^..^5).elems; # OUTPUT: «3»
method list§
multi method list(Range:D:)
Generates the list of elements that the range represents.
say (1..5).list; # OUTPUT: «(1 2 3 4 5)» say (1^..^5).list; # OUTPUT: «(2 3 4)»
method flat§
method flat(Range:D:)
Generates a Seq
containing the elements that the range represents.
method pick§
multi method pick(Range:D: --> Any:D) multi method pick(Range:D: $number --> Seq:D)
Performs the same function as Range.list.pick
, but attempts to optimize by not actually generating the list if it is not necessary.
method roll§
multi method roll(Range:D: --> Any:D) multi method roll(Range:D: $number --> Seq:D)
Performs the same function as Range.list.roll
, but attempts to optimize by not actually generating the list if it is not necessary.
method sum§
multi method sum(Range:D:)
Returns the sum of all elements in the Range. Throws X::Str::Numeric
if an element can not be coerced into Numeric.
(1..10).sum # 55
method reverse§
method reverse(Range:D: --> Seq:D)
Returns a Seq
where all elements that the Range
represents have been reversed. Note that reversing an infinite Range
won't produce any meaningful results.
say (1^..5).reverse; # OUTPUT: «(5 4 3 2)» say ('a'..'d').reverse; # OUTPUT: «(d c b a)» say (1..∞).reverse; # OUTPUT: «(Inf Inf Inf ...)»
method Capture§
method Capture(Range:D: --> Capture:D)
Returns a Capture
with values of .min
.max
, .excludes-min
, .excludes-max
, .infinite
, and .is-int
as named arguments.
method rand§
method rand(Range:D --> Num:D)
Returns a pseudo-random value belonging to the range.
say (1^..5).rand; # OUTPUT: «1.02405550417031» say (0.1..0.3).rand; # OUTPUT: «0.2130353370062»
method EXISTS-POS§
multi method EXISTS-POS(Range:D: int \pos) multi method EXISTS-POS(Range:D: Int \pos)
Returns True
if pos
is greater than or equal to zero and lower than self.elems
. Returns False
otherwise.
say (6..10).EXISTS-POS(2); # OUTPUT: «True» say (6..10).EXISTS-POS(7); # OUTPUT: «False»
method AT-POS§
multi method AT-POS(Range:D: int \pos) multi method AT-POS(Range:D: int:D \pos)
Checks if the Int
position exists and in that case returns the element in that position.
say (1..4).AT-POS(2) # OUTPUT: «3»
method raku§
multi method raku(Range:D:)
Returns an implementation-specific string that produces an equivalent object when given to EVAL.
say (1..2).raku # OUTPUT: «1..2»
method fmt§
method fmt(|c)
Returns a string where min
and max
in the Range
have been formatted according to |c
.
For more information about parameters, see List.fmt.
say (1..2).fmt("Element: %d", ",") # OUTPUT: «Element: 1,Element: 2»
method WHICH§
multi method WHICH (Range:D:)
This returns a string that identifies the object. The string is composed by the type of the instance (Range
) and the min
and max
attributes:
say (1..2).WHICH # OUTPUT: «Range|1..2»
sub infix:<+>§
multi infix:<+>(Range:D \r, Real:D \v) multi infix:<+>(Real:D \v, Range:D \r)
Takes a Real
and adds that number to both boundaries of the Range
object. Be careful with the use of parenthesis.
say (1..2) + 2; # OUTPUT: «3..4» say 1..2 + 2; # OUTPUT: «1..4»
sub infix:<->§
multi infix:<->(Range:D \r, Real:D \v)
Takes a Real
and subtract that number to both boundaries of the Range
object. Be careful with the use of parenthesis.
say (1..2) - 1; # OUTPUT: «0..1» say 1..2 - 1; # OUTPUT: «1..1»
sub infix:<*>§
multi infix:<*>(Range:D \r, Real:D \v) multi infix:<*>(Real:D \v, Range:D \r)
Takes a Real
and multiply both boundaries of the Range
object by that number.
say (1..2) * 2; # OUTPUT: «2..4»
sub infix:</>§
multi infix:</>(Range:D \r, Real:D \v)
Takes a Real
and divide both boundaries of the Range
object by that number.
say (2..4) / 2; # OUTPUT: «1..2»
sub infix:<cmp>§
multi infix:<cmp>(Range:D \a, Range:D \b --> Order:D) multi infix:<cmp>(Num(Real) \a, Range:D \b --> Order:D) multi infix:<cmp>(Range:D \a, Num(Real) \b --> Order:D) multi infix:<cmp>(Positional \a, Range:D \b --> Order:D) multi infix:<cmp>(Range:D \a, Positional \b --> Order:D)
Compares two Range
objects. A Real
operand will be considered as both the starting point and the ending point of a Range
to be compared with the other operand. A Positional
operand will be compared with the list returned by the .list
method applied to the other operand. See List infix:<cmp>
say (1..2) cmp (1..2); # OUTPUT: «Same» say (1..2) cmp (1..3); # OUTPUT: «Less» say (1..4) cmp (1..3); # OUTPUT: «More» say (1..2) cmp 3; # OUTPUT: «Less» say (1..2) cmp [1,2]; # OUTPUT: «Same»