Cody Pendant has asked for the wisdom of the Perl Monks concerning the following question:

I've got a simple script which finds words matching a regex in a large list.

There can be a large number of matches, so I wanted to write something like when you do "ls | more" and you get the output in chunks of 20 or so, hitting enter for the next chunk.

My code is a bit of a hack, with "if $x < $#list && $x+20 >= $#list"-type code everywhere. Please don't make me show it to you. It works anyway.

My question is more of a golf one, "what's the best way to work through an array over 20 items long in chunks of 20?"

I was trying to do something like this:

while(@array){ print @array[0 .. 20]; delete @array[0 .. 20]; }
But that doesn't work for various reasons which I'm sure are obvious to most monks (stop laughing!).

So, without the use of termporary vars to progressively get 0 .. 19, then 20 .. 29 and so on, how would you do it?



($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
=~y~b-v~a-z~s; print

Replies are listed 'Best First'.
Re: Working Through An Array In Chunks
by blokhead (Monsignor) on Dec 06, 2004 at 03:17 UTC
    Use splice! It not only chops out the section of the array, it returns what what was removed, and handles all the degenerate cases (when the array has fewer than 20 elements) as well.
    my @array = (1 .. 55); while ( my @chunk = splice @array, 0, 20 ) { print "<@chunk>\n"; }

    blokhead

      Thanks, that was the kind of answer I really wanted. I always forget "splice" for some reason, or get it mixed up with "slice". Why is it called "splice"? The literal meaning of that word is something more like "join", surely? Maybe it's a kind of combination of "slice" and "pop"?


      ($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
      =~y~b-v~a-z~s; print

        "splice" makes perfect sense when you're extracting middle elements from an array and thus splicing together the leading and trailing elements.

        It makes, um, a little less sense when you're using it just to remove elements from the head or tail (to be clear, I'm speaking only of the command's name, not of its appropriateness for the task.)

        Though you could argue, say, that you're splicing together array position 0 and the middle of the array when you're removing elements from the head...

Re: Working Through An Array In Chunks
by Roy Johnson (Monsignor) on Dec 06, 2004 at 03:18 UTC
    my @foo = (1..50); my @chunk; while (@chunk = splice(@foo, 0, 20)) { print "@chunk\n"; }

    Caution: Contents may have been coded under pressure.
Re: Working Through An Array In Chunks
by BrowserUk (Patriarch) on Dec 06, 2004 at 03:50 UTC

    Rather than accumulating all your data in an array and then outputting it in arbitrary sized chunks regardless of the user terminal size. Which also means the user has to wait until you've produced it all, before seeing any of it. You could use something like

    perl -wle" open OUT, '| more'; print OUT $_ for 1 .. 1000"

    Ie. Open a handle to the system/user more program and then just print the data as you produce it and let the system utility take care of the paging. This has the added value of giving the user access to all the system utility's facilities, like backing up, search etc.


    Examine what is said, not who speaks.
    "But you should never overestimate the ingenuity of the sceptics to come up with a counter-argument." -Myles Allen
    "Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo         "Efficiency is intelligent laziness." -David Dunham
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: Working Through An Array In Chunks
by davido (Cardinal) on Dec 06, 2004 at 05:28 UTC

    There are always old-school C-style loops:

    use strict; use warnings; my @array = ( 0 .. 50 ); for ( my $idx = 0; $idx <= $#array; $idx += 20 ) { my $top = $idx + 19; $top = ( $top <= $#array ) ? $top : $#array; print "Processing elements $idx through $top.\n"; foreach my $element ( @array[ $idx .. $top ] ) { # Process each $element here. ...for example... print $element, " "; } print "\n\tDone with $idx through $top.\n\n"; }

    Dave

Re: Working Through An Array In Chunks
by revdiablo (Prior) on Dec 06, 2004 at 21:13 UTC

    I would probably approach it as follows:

    use List::Util qw(min); my @array = 13 .. 234; for (0 .. $#array/20) { my $beg = $_ * 20; my $end = min($beg + 19, $#array); print for @array[ $beg .. $end ]; <STDIN> unless $end >= $#array; }