class Range

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␤»
    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: «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

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␤»

See Also

class Array

Sequence of itemized values

class Bag

Immutable collection of distinct objects with integer weights

class BagHash

Mutable collection of distinct objects with integer weights

class Capture

Argument list suitable for passing to a Signature

class Hash

Mapping from strings to itemized values

class IterationBuffer

Low level storage of positional values

class List

Sequence of values

class Map

Immutable mapping from strings to values

class Mix

Immutable collection of distinct objects with Real weights

class MixHash

Mutable collection of distinct objects with Real weights

class NFC

Codepoint string in Normal Form C (composed)

class NFD

Codepoint string in Normal Form D (decomposed)

class NFKC

Codepoint string in Normal Form KC (compatibility composed)

class NFKD

Codepoint string in Normal Form KD (compatibility decomposed)

class Pair

Key/value pair

class PseudoStash

Stash type for pseudo-packages

class Seq

An iterable, potentially lazy sequence of values

class Set

Immutable collection of distinct objects

class SetHash

Mutable collection of distinct objects

class Slip

A kind of List that automatically flattens into an outer container

class Stash

Table for "our"-scoped symbols

class Uni

A string of Unicode codepoints

class utf8

Mutable uint8 buffer for utf8 binary data

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.