Perl 6 has lazy lists, but it also has unbounded Range objects. Which one should you choose for counting up by whole numbers?
And there's unbounded Range with two dots:
0 .. *
There's the Seq (sequence) with three dots:
0 ... *
A Range
generates lists of consecutives thingys using their natural order. It inherits from Iterable, but also Positional so you can index a range. You can check if something is within a Range
, but that's not part of the task.
A Seq
can generate just about anything you like as long as it knows how to get to the next element. It inherits from Iterable, but also PositionalBindFailover which fakes the Positional
stuff through a cache and list conversion. I don't think that a big deal if you're only moving from one element to the next.
I'm going back and forth on this. At the moment I'm thinking it's Range.
Semantically speaking, a Range
is a static thing (a bounded set of values), a Seq
is a dynamic thing (a value generator) and a lazy List
a static view of a dynamic thing (an immutable cache for generated values).
Rule of thumb: Prefer static over dynamic, but simple over complex.
In addition, a Seq
is an iterable thing, a List
is an iterable positional thing, and a Range
is an ordered iterable positional thing.
Rule of thumb: Go with the most generic or most specific depending on context.
As we're dealing with iteration only and are not interested in positional access or bounds, using a Seq
(which is essentially a boxed Iterator
) seems like a natural choice. However, ordered sets of consecutive integers are exactly what an integer Range
represents, and personally that's what I would see as most appropriate for your particular use case.
When there is no clear choice, I tend to prefer ranges for their simplicity anyway (and try to avoid lazy lists, the heavy-weight).
Note that the language syntax also nudges you in the direction of Range
, which are rather heavily Huffman-coded (two-char infix ..
, one-char prefix ^
).
Both 0 .. *
and 0 ... *
are fine.
for
loop, has exactly the same effect in both cases. (Neither will leak memory by keeping around already iterated elements.)@
variable produces the same lazy Array.So as long as you only want to count up numbers to infinity by a step of 1, I don't see a downside to either.
The ...
sequence construction operator is more generic though, in that it can also be used to
1, 3 ... *
)10 ... -Inf
)2, 4, 8 ... *
)1, 1, *+* ... *
)so when I need to do something like that, then I'd consider using ...
for any nearby and related "count up by one" as well, for consistency.
On the other hand:
Range
can be indexed efficiently without having to generate and cache all preceding elements, so if you want to index your counter in addition to iterating over it, it is preferable. The same goes for other list operations that deal with element positions, like reverse
: Range
has efficient overloads for them, whereas using them on a Seq
has to iterate and cache its elements first.1 .. $n
), it's safer to use a Range
because you can be sure it'll never count downwards, no matter what $n
is. (If the endpoint is less than the startpoint, as in 1 .. 0
, it will behave as an empty sequence when iterated, which tends to get edge-cases right in practice.)reverse 1 .. $n
.Range
is a more specific/high-level representation of the concept of "numbers from x to y", whereas a Seq
represents the more generic concept of "a sequence of values". A Seq
is, in general, driven by arbitrary generator code (see gather
/take
) - the ...
operator is just semantic sugar for creating some common types of sequences. So it may feel more declarative to use a Range when "numbers from x to y" is the concept you want to express. But I suppose that's a purely psychological concern... :PIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With