/
Range.pod6
348 lines (226 loc) · 11.7 KB
/
Range.pod6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
=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: «12345»
('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: «4815»
my @range = 0..2;
.say for @numbers[@range]; # OUTPUT: «4815»
=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
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|/type/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|/type/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|/type/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