Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to be aware of missing values during multi-value iteration in Perl 6?

During multi-value iteration, if we run out of values, the last group of values will not be handled in the current version of Rakudo.

my @arr = 1, 2, 3, 4, 5, 6
for @arr -> $a, $b, $c, $d {
  say $a
  say $b
  say $c
  say $d
}

The result is

1
2
3
4

, which drops 5 and 6.

So in which way I am able get the elements that are dropped?

like image 768
hwding Avatar asked May 28 '17 07:05

hwding


2 Answers

As @choroba's reply notes, your code actually throws an error in Perl 6.c.

The error occurs on the second iteration, when only 5, 6 is left from the input array, which can't be mapped to the signature $a, $b, $c, $d because all four parameters in that signature are mandatory.

There are multiple ways to make it work:

A) Mark the parameters as optional.

for @arr -> $a, $b?, $c?, $d? {
    say "---";
    say $a;
    say $b if $b.defined;
    say $c if $c.defined;
    say $d if $d.defined;
}

The ? means optional – i.e. when no argument is passed to that parameter, it's set to the value Mu ("most undefined"). Note that there's no need to mark the first parameter as optional, because when there are zero input elements left the for loop terminates and doesn't try to enter another iteration.
Output:

---
1
2
3
4
---
5
6

B) Specify defaults for the parameters.

for @arr -> $a, $b = 'default-B', $c = 'default-C', $d = 'default-D' {
    say ($a, $b, $c, $d);
}

This is what @choroba already suggested. It's essentially another way of making the parameters optional, but now with a custom value instead of Mu in case no argument was passed to them.
Output:

(1 2 3 4)
(5 6 default-C default-D)

C) Use .rotor to handle the partitioning.

Using a for block with a multi-parameter signature is not the only way to iterate a list of values n-at-a-time. The .rotor method provides the same functionality in a more functional style. It skips partial iterations at the end by default, so you need to give it the :partial option to include that iteration:

for @arr.rotor(4, :partial) -> @slice {
    say @slice;
}

For each iteration, @slice becomes a List of 4 elements, except for the last iteration where it can have fewer elements.. (You could check @slice.elems inside the loop body to see how many you got.)
Output:

(1 2 3 4)
(5 6)
like image 64
smls Avatar answered Sep 30 '22 04:09

smls


Trying in Rakudo Star I've just installed:

===SORRY!=== Error while compiling /home/choroba/1.p6
Unexpected block in infix position (missing statement control word before the expression?)
at /home/choroba/1.p6:2
------> for @arr⏏ -> $a, $b, $c, $d {
    expecting any of:
        infix
        infix stopper

After adding semicolons everywhere, I'm getting

1
2
3
4
Too few positionals passed; expected 4 arguments but got 2
  in block <unit> at /home/choroba/1.p6 line 2

You can provide default values if you don't want to get an error:

for @arr -> $a,
            $b = 'Missing b',
            $c = 'Missing c',
            $d = 'Missing d' {

As smls comments, you can also make the variables optional:

for @arr -> $a, $b?, $c?, $d? {

which will work the same as using $var = Mu.

like image 42
choroba Avatar answered Sep 30 '22 05:09

choroba