Is it possible to assign two variables in Perl foreach loop?

后端 未结 4 2281
旧时难觅i
旧时难觅i 2021-02-19 00:40

Is it possible to assign two variables the same data from an array in a Perl foreach loop?

I am using Perl 5, I think I came across something in Perl 6.

Somethi

4条回答
  •  暖寄归人
    2021-02-19 01:39

    Here is a module-less way to "loop" by an arbitrary value ($by) and output the resulting group of elements using an array slice:

    #!perl -l
    @array = "1".."6"; 
    $by = 3; $by--; 
    
    for (my $i = 0 ; $i < @array ; $i += $by ) { 
          print "@array[$i..$i+$by]"; 
          $i++ ;
    }
    

    As a one-liner to test (cut and paste to a Unix shell):

    perl -E '@array = "1".."6"; $by = 3; $by--; 
          for (my $i = 0 ; $i < @array ; $i += $by ) { 
          say "@array[$i..$i+$by]"; $i++ }'
    

    Output:

    1 2 3
    4 5 6
    

    If you make $by = 2; it will print pairs of numbers. To get at specific elements of the resulting slice access it as an anonymous array: (e.g. [@array[$i..$i+$by]]->[1]).

    See also:

    • How do I read two items at a time in a Perl foreach loop?
    • Perl way of iterating over 2 arrays in parallel

    Some good responses there, including reference to natatime which is quite easy to use. It's easy to implement too - it is essentially a wrapper around the splice solutions mentioned in the responses here.

    The following is not the nicest example, but I've been using autobox::Core and made an @array->natatime() "method" ;-) like this:

    use autobox::Core ;
    sub autobox::Core::ARRAY::natatime {
      my ($self, $by) = @_; 
      my @copy = @$self ;
      my @array ;
    
      push @array, [splice (@copy, 0, $by) ] while @copy ;
    
      if  ( not defined wantarray ) { 
          print "@{ $_ } \n" for @array ;
      } 
    
      return wantarray ? @array : \@array;      
    }
    

    The @copy array is spliced destructively, but $self (which is how the @array in front of the autobox method -> arrow gets passed to the function) is still there. So I can do:

    my @dozen = "1" .. "12" ;           # cakes to eat
    @dozen->natatime(4)  ;              # eat 4 at time
    my $arr_ref = @dozen->natatime(4) ; # make a reference
    say "Group 3: @{ $arr_ref->[2] }" ; # prints a group of elements
    say scalar @dozen , " cakes left" ; # eat cake; still have it
    

    Output:

    1 2 3 4 
    5 6 7 8 
    9 10 11 12 
    Group 3: 9 10 11 12
    12 cakes left
    

    One other approach that also uses a CPAN module (I gave this answer elsewhere but it is worth repeating). This can also be done non-destructively, with Eric Strom's excellent List::Gen module:

    perl -MList::Gen=":all" -E '@n = "1".."6"; say "@$_" for every 2 => @n'
    1 2
    3 4
    5 6
    

    Each group of elements you grab is returned in an anonymous array so the individual values are in: $_->[0] $_->[1] ... etc.


    You mentioned Perl6, which handles multiple looping values nicely:

    my @qarr = 1 .. 6;
    my ($x, $y, $z) ;
    for @qarr -> $x , $y , $z { say $x/$y  ; say "z = " ~ $z }
    

    Output:

    0.5
    z = 3
    0.8
    z = 6
    

    For more on the Perl6 approach see: Looping for Fun and Profit from the 2009 Perl6 Advent Calendar, or the Blocks and Statements Synopsis for details. Perhaps Perl 5 will have a similar "loop by multliple values" construct one day - à la perl5i's foreach :-)

提交回复
热议问题