Search code examples
listlazy-evaluationrakulazy-sequencesrakudo

how to cycle through list indefinitely and lazily in Raku?


This is mostly to geek out on how awesome Raku is.

Question

Are there built-in methods that will take a list and cycle through it indefinitely, producing, say, the lazy list

a, b, c, a, b, c, ...

out of (a, b, c)? Nothing in the documentation on lists seems to obviously do the trick.

Possible solutions

I can think of at least a couple.

The more plodding down-to-earth method would be to map @array[<variable> mod length-of-@array] over the lazy range 0..Inf. In the perl6 REPL:

> my @ar=<a b c>
[a b c]
> (0..Inf).map({ @ar[$_ % @ar.elems] }).[0..100]
(a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a ...)

A cooler (I think) solution would have you first turn your list into a slip, and then apply the repetition operator to that slip indefinitely:

> my @ar=<a b c>
[a b c]
> (|@ar xx *).[0..100]
(a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a ...)

Conclusion

Even though I can achieve this (and the last solution in particular is very pithy and satisfying), what I am wondering is whether I'm missing anything built in specifically for this purpose.


Edit Re: the accepted answer

This is to elaborate slightly on the answer by @Brad Gilbert. The «~» operator wraps string concatenation ~ in the « » hyper operator, which results in applying the wrapped binary operator to elements selected sequentially from the two lists.

So to achieve what I wanted (list-cycling to a desired length, like say 100), one would do

<a b c> <<~>> ("" xx 100)

This produces

(a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a b c a)

(100 entries). It does have a couple of drawbacks though:

  • it coerces the list entries to strings, due to the application of ~
  • it doesn't actually produce a lazy infinite list:
<a b c> <<~>> ("" xx *)

returns

List on right side of hyperop of infix:<~> is known to be infinite
  in block <unit> at <unknown file> line 1

Solution

  • When it can be written this short, why add a feature just for that. Especially since it is probably a rare event that you would need such a thing.

    |< a b c > xx *
    

    Well there is one exception, if you use something like «~» it extend it out for you.

    < a b c > «~» (1..10)
    # (a1 b2 c3 a4 b5 c6 a7 b8 c9 a10)