Permalink
Fetching contributors…
Cannot retrieve contributors at this time
349 lines (226 sloc) 11.7 KB
=begin pod
=TITLE class Range
=SUBTITLE Interval of ordered values
=for code :skip-test<compile time error>
class Range is 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 C<list> method) uses the same semantics as
the C<++> prefix and postfix operators, i.e., it calls the C<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; # RESULT: «'b', 'c', 'd', 'e'»
5 ~~ ^5; # RESULT: «False»
4.5 ~~ 0..^5; # RESULT: «True»
(1.1..5).list; # RESULT: «(1.1, 2.1, 3.1, 4.1)»
Use the L<...|/language/operators#infix_...> 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 C<> or C<*> (Whatever) to indicate an end point to be open-ended.
=for code
for 1..* { .say }; # start from 1, continue until stopped
for 1..∞ { .say }; # the same
=head2 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 a 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␤»
=head2 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␤»
=head2 Matching against Ranges
You can use L<smartmatch|/routine/~~> to match against Ranges.
say 3 ~~ 1..12; # OUTPUT: «True␤»
say 2..3 ~~ 1..12; # OUTPUT: «True␤»
X<|in-range>
I<In Rakudo only>, you can use the C<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 C<False>:
say ('א'..'ת').in-range('ע');# OUTPUT: «True␤»
However, if it is not included in the range:
=for code :skip-test<Raises an exception>
say ('א'..'ת').in-range('p', "Letter 'p'");
# OUTPUT: «(exit code 1) Letter 'p' out of range. Is: "p", should be in "א".."ת"␤
The second parameter to C<in-range> is the optional message that will be printed
with the exception. It will print C<Value> by default.
=head1 Methods
=head2 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 C<Range> contains (overlaps with) another C<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 )
Of course, an infinite C<Range> always contains another C<Range>, therefore:
say 1..10 ~~ -∞..∞; # OUTPUT: «True␤»
say 1..10 ~~ -∞^..^∞; # OUTPUT: «True␤»
Similarly, a C<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 'perl' ~~ -∞^..^∞; # OUTPUT: «True␤»
say 'perl' ~~ -∞..∞; # OUTPUT: «True␤»
say 'perl' ~~ 1..*; # OUTPUT: «True␤»
When smartmatching a C<Range> of integers with a L<Cool> (string)
the C<ACCEPTS> methods exploits the L<before|/routine/before>
and L<after|/routine/after> operators in order to check that
the C<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 C<'5'> string is I<after> the C<10> integer value,
the C<Range> does not overlap with the specified value.
When matching with a C<Mu> instance (i.e., a generic instance),
the L<cmp|/routine/cmp> operator is used.
=head2 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␤»
=head2 method excludes-min
method excludes-min(Range:D: --> Bool:D)
Returns C<True> if the start point is excluded from the range, and C<False>
otherwise.
say (1..5).excludes-min; # OUTPUT: «False␤»
say (1^..^5).excludes-min; # OUTPUT: «True␤»
=head2 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␤»
=head2 method excludes-max
method excludes-max(Range:D: --> Bool:D)
Returns C<True> if the end point is excluded from the range, and C<False>
otherwise.
say (1..5).excludes-max; # OUTPUT: «False␤»
say (1^..^5).excludes-max; # OUTPUT: «True␤»
=head2 method bounds
method bounds(Range:D: --> Positional)
Returns a list consisting of the start and end point.
say (1..5).bounds; # OUTPUT: «(1 5)␤»
say (1^..^5).bounds; # OUTPUT: «(1 5)␤»
=head2 method infinite
method infinite(Range:D: --> Bool:D)
Returns C<True> if either end point was declared with C<> or C<*>.
say (1..5).infinite; # OUTPUT: «False␤»
say (1..*).infinite; # OUTPUT: «True␤»
=head2 method is-int
method is-int(Range:D: --> Bool:D)
Returns C<True> if both end points are C<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␤»
=head2 method int-bounds
method int-bounds(Range:D: --> Positional)
If the C<Range> is an integer range (as indicated by L<is-int>), then this
method returns a list with the first and last value it will iterate over
(taking into account L<excludes-min> and L<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)␤»
=head2 method minmax
Defined as:
multi method minmax(Range:D: --> List:D)
If the C<Range> is an integer range (as indicated by L<is-int>), then this
method returns a list with the first and last value it will iterate over
(taking into account L<excludes-min> and L<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 L<excludes-min> or
L<excludes-max> are C<True> in which case a L<Failure|/type/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␤»
=head2 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 C<List>. Returns Inf if either end point was specified
as C<Inf> or C<*>.
say (1..5).elems; # OUTPUT: «5␤»
say (1^..^5).elems; # OUTPUT: «3␤»
=head2 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)␤»
=head2 method flat
method flat(Range:D: --> List:D)
Generates the list of elements that the range represents.
=head2 method pick
multi method pick(Range:D: --> Any:D)
multi method pick(Range:D: $number --> Seq:D)
Performs the same function as C<Range.list.pick>, but attempts to optimize
by not actually generating the list if it is not necessary.
=head2 method roll
multi method roll(Range:D: --> Any:D)
multi method roll(Range:D: $number --> Seq:D)
Performs the same function as C<Range.list.roll>, but attempts to optimize
by not actually generating the list if it is not necessary.
=head2 method sum
multi method sum(--> Numeric:D)
Returns the sum of all elements in the Range. Throws L<X::Str::Numeric> if an element can not be coerced into Numeric.
(1..10).sum # 55
=head2 method reverse
method reverse(Range:D: --> Seq:D)
Returns a L<Seq> where all elements that the C<Range> represents have been
reversed. Note that reversing an infinite C<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 ...)␤»
=head2 method Capture
Defined as:
method Capture(Range --> Capture:D)
Returns a L<Capture> with values of L«C<.min>|/type/Range#method_min»
L«C<.max>|/type/Range#method_max»,
L«C<.excludes-min>|/type/Range#method_excludes-min»,
L«C<.excludes-max>|/type/Range#method_excludes-max»,
L«C<.infinite>|/type/Range#method_infinite», and
L«C<.is-int>|/type/Range#method_is-int» as named arguments.
=head2 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␤»
=end pod
# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6