How can I partition a Perl array into equal sized chunks?

后端 未结 9 1462
眼角桃花
眼角桃花 2020-12-15 08:01

I have a fixed-sized array where the size of the array is always in factor of 3.

my @array = (\'foo\', \'bar\', \'qux\', \'foo1\', \'bar\', \'qux2\', 3, 4, 5         


        
9条回答
  •  伪装坚强ぢ
    2020-12-15 08:41

    As a learning experience I decided to do this in Perl6

    The first, perhaps most simplest way I tried was to use map.

    my @output := @array.map: -> $a, $b?, $c? { [ $a, $b // Nil, $c // Nil ] };
    .say for @output;
    
    foo bar qux
    foo1 bar qux2
    3 4 5
    

    That didn't seem very scalable. What if I wanted to take the items from the list 10 at a time, that would get very annoying to write. ... Hmmm I did just mention "take" and there is a keyword named take lets try that in a subroutine to make it more generally useful.

    sub at-a-time ( Iterable \sequence, Int $n where $_ > 0 = 1 ){
      my $is-lazy = sequence.is-lazy;
      my \iterator = sequence.iterator;
    
      # gather is used with take
      gather loop {
        my Mu @current;
        my \result = iterator.push-exactly(@current,$n);
    
        # put it into the sequence, and yield
        take @current.List;
    
        last if result =:= IterationEnd;
      }.lazy-if($is-lazy)
    }
    

    For kicks let's try it against an infinite list of the fibonacci sequence

    my $fib = (1, 1, *+* ... *);
    my @output = at-a-time( $fib, 3 );
    .say for @output[^5]; # just print out the first 5
    
    (1 1 2)
    (3 5 8)
    (13 21 34)
    (55 89 144)
    (233 377 610)
    

    Notice that I used $fib instead of @fib. It was to prevent Perl6 from caching the elements of the Fibonacci sequence.
    It might be a good idea to put it into a subroutine to create a new sequence everytime you need one, so that the values can get garbage collected when you are done with them.
    I also used .is-lazy and .lazy-if to mark the output sequence lazy if the input sequence is. Since it was going into an array @output it would have tried to generate all of the elements from an infinite list before continuing onto the next line.


    Wait a minute, I just remembered .rotor.

    my @output = $fib.rotor(3);
    
    .say for @output[^5]; # just print out the first 5
    
    (1 1 2)
    (3 5 8)
    (13 21 34)
    (55 89 144)
    (233 377 610)
    

    .rotor is actually far more powerful than I've demonstrated.

    If you want it to return a partial match at the end you will need to add a :partial to the arguments of .rotor.

提交回复
热议问题