Search code examples
arraysfor-loopraku

How to destructure for loop parameters into array of fixed size?


I am trying to put the parameters in a for loop into array of FIXED size. This is what I have been doing (I want to use a array @m of 3 elements):

for (1..19).rotor(3, :partial) -> @m { say @m; } # works, but I cannot specify size of @m

However, all of the following give me errors:

for (1..19).rotor(3, :partial) -> @m[0,1,2] { say @m; }
===SORRY!=== Error while compiling:
Variable '@m' is not declared
------> ).rotor(3, :partial) -> @m[0,1,2] { say ⏏@m; }

for (1..19).rotor(3 => -2) -> @m[0..2] { say @m; }
===SORRY!=== Error while compiling:
Variable '@m' is not declared
------> 1..19).rotor(3 => -2) -> @m[0..2] { say ⏏@m; }

for (1..19).rotor(3 => -2) -> @m[3] { say $_; say @m; }
===SORRY!=== Error while compiling:
Variable '@m' is not declared
------> ).rotor(3 => -2) -> @m[3] { say $_; say ⏏@m; }

So, how should I specify that the array @m must have only 3 elements?


Solution

  • The title of the question mentions destructuring, but that is about taking something apart. For example, we could extract the three elements using it:

    for (1..19).rotor(3, :partial) -> [$a, $b, $c] {
    }
    

    However, this doesn't seem to actually be a destructuring problem, since the request isn't to break the passed aggregate into its parts, but transform it into a different type of aggregate.

    Looking a little further at the question:

    I am trying to put the parameters in a for loop into array of FIXED size.

    The thing is, rotor doesn't produce (mutable) Arrays at all. Instead, when writing:

    for (1..19).rotor(3, :partial) -> @m {
    }
    

    Then @m is a List. A List is immutable (and thus implicitly its size is fixed at creation), so if the intent is that there can be no accidental change of size, that's already a cert. Unfortunately, the ultimate goal wasn't stated.

    If really wanting to turn the passed immutable List into a shaped Array, there's nothing for it other than to assign it into a new fixed size Array:

    for (1..19).rotor(3, :partial) -> @l {
        my @arr[3] = @l;
    }
    

    Of course, this is going to blow up if the :partial leads to leftover elements; one could do:

    for (1..19).rotor(3, :partial) -> @l {
        my @arr[@l.elems] = @l;
    }
    

    To avoid that. However, if the goal as really to ensure that things blow up if there ever are leftover elems, then either a where:

    for (1..19).rotor(3, :partial) -> @m where .elems == 3 {
    }
    

    Or, a bit less clearly, a throw-away destructure:

    for (1..19).rotor(3, :partial) -> @m [$,$,$] {
    }
    

    Would do it.