tags:

views:

225

answers:

5

I have an array in Perl I want to print with space delimiters between each element, except every 10th element which should be newline delimited. There aren't any spaces in the elements if that matters.

I've written a function to do it with for and a counter, but I wondered if there's a better/shorter/canonical Perl way, perhaps a special join syntax or similar.

My function to illustrate:

sub PrintArrayWithNewlines
{
    my $counter = 0;
    my $newlineIndex = shift @_;

    foreach my $item (@_)
    {
        ++$counter;
        print "$item";
        if($counter == $newlineIndex)
        {
            $counter = 0;
            print "\n";
        }
        else
        {
            print " ";
        }
    }
}
+4  A: 

You can use List::MoreUtils::natatime:

use warnings; use strict;

use List::MoreUtils qw( natatime );

my @x = (1 .. 35);

my $it = natatime 10, @x;

while ( my @v = $it->() ) {
    print "@v\n"
}

Output:

C:\Temp> x
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35
Sinan Ünür
natatime is really cool. I just wish I didn't read it as "nata time". Nata!
Robert P
+4  A: 

If you do not want to use any external modules, you can use array slices:

use warnings; use strict;

my @x = (1 .. 95);
my $n = 10;

for my $i ( 0 .. int @x/$n ) {
    no warnings 'uninitialized';
    print "@x[$n * $i .. $n * ($i + 1) - 1]\n";
}
Sinan Ünür
Should be `$n*($i+1)-1`
mobrule
@mobrule Thank you.
Sinan Ünür
And, when you want to change the delimiter, just set $"
brian d foy
This is non-destructive to the array, and therefore, IMO, preferable to the `splice` since you can use it with an array reference. I'd use `natatime` unless I was unable to use List::MoreUtils for some reason.
daotoad
+1  A: 

You can also use map with a modification to PrintArrayWithNewlines:

#!/usr/bin/perl -w

use strict;

sub PrintArrayWithNewlines 
{    
    my @array = @_;
    my $newlineIndex = 10;

    foreach my $item (@array) {
        ++$globalCounter;
        print "$item";
        if ($globalCounter == $newlineIndex) {
            $globalCounter = 0;
            print "\n";
        }
        else {
            print " ";
        }
    }
}

my $globalCounter = 0;
my @myArray = 'a' .. 'z'
map { PrintArrayWithNewlines($_) } @myArray;
print "\n";

The output would be:

$ ./test.pl
a b c d e f g h i j
k l m n o p q r s t
u v x y z
Alex Reynolds
`my @myArray = 'a' .. 'z'` *don't type any more than necessary*.
Sinan Ünür
Thanks, I added the change.
Alex Reynolds
`print map({ not( ($_ + 1) % 10) ? "$x[$_]\n" : "$x[$_] " } 0 .. $#x), "\n";`
Sinan Ünür
That's a great answer, you should post it!
Alex Reynolds
+3  A: 

I like splice for a job like this:

sub PrintArrayWithNewlines {
    my $n = 10;
    my $delim = " ";
    while (my @x = splice @_, 0, $n) {
        print join($delim, @x), "\n";
    }
}
mobrule
+2  A: 

The functions by and every in my module List::Gen can solve this problem:

use List::Gen;

for (every 10 => 'a' .. 'z') {
    print "@$_\n"
}

#   a b c d e f g h i j
#   k l m n o p q r s t
#   u v w x y z

it can also be written

foreach (by 10 => 'a' .. 'z') {
    print "@$_\n"
}

or using the functional form:

mapn {print "@_\n"} 10 => 'a' .. 'z';  # @_ not @$_ here

or an iterator if that's your style:

my $letters = by 10 => 'a' .. 'z';

while (my $line = $letters->next) {
   print "@$line\n";
}
Eric Strom