T M
class Range
Error ReportCollection examples

Interval of ordered values

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: «1␤2␤3␤4␤5␤» 
('a' ^..^ 'f').list;     # OUTPUT: «'b', 'c', 'd', 'e'» 
5 ~~ ^5;                 # OUTPUT: «False» 
4.5 ~~ 0..^5;            # OUTPUT: «True» 
(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: «4␤8␤15␤» 
my @range = 0..2;
.say for @numbers[@range]; # OUTPUT: «4␤8␤15␤»

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

Defined as

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.new35  );
my $r = Range.new110 );
 
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 )

Of course, an infinite Range always contains another 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-boundsmy $minmy $max{
    say "$min$max" ; # OUTPUT: «3, 5␤» 
}
else {
    say "Could not determine integer bounds";
}

method minmax

Defined as:

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

method list(Range:D: --> List: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: --> List:D)

Generates the list of 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(--> Numeric: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

Defined as:

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

Defined as:

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

Defined as

multi method EXISTS-POS(Range:D: int \pos)
multi method EXISTS-POS(Range:D: Int \pos)

Returns True if pos is greater or equal than 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

Defined as

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

Defined as

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

Defined as

method fmt(|c)

Returns a string where min and max in the Range have been formatted according to |c.

For more information about formats strings, see sprintf.

say (1..2).fmt("Element: %d"","# OUTPUT: «Element: 1,Element: 2␤»

method WHICH

Defined as

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 sub infix:<+>(Range:D \rReal:D \v)
multi sub infix:<+>(Real:D \vRange:D \r)

Takes an 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 sub infix:<->(Range:D \rReal:D \v)

Takes an 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 sub infix:<*>(Range:D \rReal:D \v)
multi sub infix:<*>(Real:D \vRange:D \r)

Takes an Real and multiply both boundaries of the Range object by that number.

say (1..2* 2# OUTPUT: «2..4␤»

sub infix:</>

multi sub infix:</>(Range:D \rReal:D \v)

Takes an Real and divide both boundaries of the Range object by that number.

say (2..4/ 2# OUTPUT: «1..2␤»

sub infix:<cmp>

multi sub infix:<cmp>(Range:D \aRange:D \b --> Order:D)
multi sub infix:<cmp>(Num(Real) \aRange:D \b --> Order:D)
multi sub infix:<cmp>(Range:D \aNum(Real) \b --> Order:D)
multi sub infix:<cmp>(Positional \aRange:D \b --> Order:D)
multi sub infix:<cmp>(Range:D \aPositional \b --> Order:D)

Compares two Range objects. If you use a Real, it will be compared to the Range b..b. If you use a Positional.

say (1..2cmp (1..2); # OUTPUT: «Same␤» 
say (1..2cmp (1..3); # OUTPUT: «Less␤» 
say (1..4cmp (1..3); # OUTPUT: «More␤» 
say (1..2cmp 3;      # OUTPUT: «Less␤» 
say (1..2cmp [1,2];  # OUTPUT: «Same␤»