views:

112

answers:

7

Hello, is there a module which gives me an output of two columns ( or more ) on STDOUT?

#!/usr/bin/env perl
use warnings;
use strict;

printf "%0.3d\n", $_ for 1 .. 100; 

I'd like to have 1-50 in the first column and 51-100 in the second.

+4  A: 

There might be a module for this sort of thing ... or you can roll your own:

use strict;
use warnings;

my $n_cols = shift @ARGV;
my @list = (1..100);
my $part_size = @list / $n_cols;
$part_size = int($part_size + 1) unless $part_size == int($part_size);
my $fmt = '%8s' x $n_cols . "\n";

for my $i (0 .. $part_size - 1){
    my @vals = map { defined($list[$_]) ? $list[$_] : '' }
               map { $_ * $part_size + $i }
               0 .. $n_cols;
    printf $fmt, @vals;

}
FM
+1 for a generalized solution, but I'd be happier if you had at least a few words explaining that no, there is no module, but you can use my code here, or something to that effect. I find a code-only answer to a "Is there a module?" question slightly jarring.
Adam Bellaire
+4  A: 

You can use Perl formatting to do this.

Here is a link with a tutorial on it.

Here is a snippet from that page:

format MYFILE = 
Name: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<        Salary: @###########.##
      $name,                                       $salary
.

Produces the output:

Name: John Smith                           Salary:        78293.22

The formatting characters have the following formatting characteristics:

>   right justified
#   right justified (numeric only; can include a decimal point) 
<   left justified 
|   center justified
*   left justified, fill in all data from value


Edit:

Here is a more direct answer to your question:

#!/usr/bin/perl

use strict;

my @data = (1..100);
my ($v1, $v2);

# Create formats
format ONECOL = 
Col1: @###
      $v1
.
format TWOCOL = 
Col1: @###        Col2: @###
      $v1,        $v2
.

# Set the two column format
$~ = "TWOCOL";

my $i = 0;
my $middle = int(@data/2) + (@data % 2);
for (my $x = $middle; $x < @data; $i++, $x++)
{
    $v1 = $data[$i];
    $v2 = $data[$x];
    write;
}

# Set the format to one column
if ($i < $middle)
{
    $~ = "ONECOL";
    $v1 = $data[$i];
    write;    
}
RC
This doesn't answer the question: He doesn't want to generate two separate lists side by side. He wants to have two columns of the *same* data, where the listing stops at the end of the first column and continues from the top of the second.
Adam Bellaire
I've updated my answer based on your comment
RC
+1  A: 

Can you do it this way?

for ($count = 1; $count <=100; $count++) {
    printf "%d %d\n", $count, $count+50;
}
ghostdog74
`for my $count ( 1 .. 50 )` ;-)
Sinan Ünür
+1  A: 

Take a look at the column utility. I don't think you can ask for a specific number of columns, though.

jleedev
+2  A: 

This is begging for iterators (not! but it's fun). Note that the revised solution allows you to customize the number of columns as well.

#!/usr/bin/perl

use strict;
use warnings;

use List::AllUtils qw( min );

print_cols('%04d', 1, 100, 11);

sub print_cols {
    my ($fmt, $min, $max, $cols) = @_;
    my $its = partition($min, $max, $cols);

    while ( (my @vals = grep { defined } map {$_->()} @$its)) {
        printf join(' ', ($fmt) x @vals) . "\n", @vals;
    }

    return;
}

sub make_asc_it {
    my ($min, $max) = @_;
    return sub {
        return unless $min <= $max;
        return $min ++;
    }
}

sub partition {
    my ($min, $max, $cols) = @_;
    return unless $min <= $max;

    my $rows = sprintf '%.0f', ($max - $min) / $cols;
    my @its;

    for my $col (1 .. $cols) {
        push @its, make_asc_it(
            min( $min, $max ),
            min( $min + $rows - 1, $max )
        );
        $min += $rows;
    }
    push @its, make_asc_it($min, $max) if $min <= $max;

    return \@its;
}

Output for eleven columns:

0001 0010 0019 0028 0037 0046 0055 0064 0073 0082 0091 0100
0002 0011 0020 0029 0038 0047 0056 0065 0074 0083 0092
0003 0012 0021 0030 0039 0048 0057 0066 0075 0084 0093
0004 0013 0022 0031 0040 0049 0058 0067 0076 0085 0094
0005 0014 0023 0032 0041 0050 0059 0068 0077 0086 0095
0006 0015 0024 0033 0042 0051 0060 0069 0078 0087 0096
0007 0016 0025 0034 0043 0052 0061 0070 0079 0088 0097
0008 0017 0026 0035 0044 0053 0062 0071 0080 0089 0098
0009 0018 0027 0036 0045 0054 0063 0072 0081 0090 0099
Sinan Ünür
+4  A: 

Text::Column

use Text::Column qw(format_array_table);

print format_array_table([map [$_, 50+$_], 1..50], [6, 6], [qw(first second)]);

Or, if you're on a UNIX system, pipe through pr(1).

open COLUMN, '|-', qw(pr -T2);
print COLUMN "$_\n" for 1..100;
ephemient
For the accepted answer I had to choose between your “open COLUMN, '|-', qw(pr -T2)” and draegtun’s “Perl6::Form”-example.( With your Text::Column-example I get something like a endless loop. Your “open COLUMN”-example works with “column” too, so you helped jleedev ( without example ) earn a point ).Perl6::Form-example:+ Perl only+ Perl6-bonus+ no loop needed+ formatting possibilities- have to create the values for the columns by hand"open COLUMN, '|-', qw(pr -T2);"-example:- not Perl only+ less work to do
sid_com
@sid_com: Sorry that I didn't test the Text::Column example; I don't have the module installed and I was going just off of the documentation. I think I fixed it... it seems to be not well-suited for your requirements after all.
ephemient
+2  A: 

Have a look at Perl6::Form

use Perl6::Form;

my $col1 = [ 1..50 ];
my $col2 = [ 51..100 ];

print form '{[} {[}', $col1, $col2;


So you can create a helper sub to produce the correct form which could go something like this:

sub mk_form {
    my $rows = shift;
    my @form;

    # calculate columns needed 
    use integer;
    my $cols = scalar @_ / $rows;
    $cols++ if scalar @_ % $rows;

    # create Perl6::Form args
    push @form, '{[} ' x $cols;
    push @form, [ splice @_, 0, $rows ] for 1..$cols;

    return @form;
}

Then to produce a columned page at 50 rows:

say form mk_form(50, 1..101);

/I3az/

PS: See SO question What other languages have features and/or libraries similar to Perl’s format? for a bit more about Perl6::Form.

draegtun