Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Long array -> multiple columns?

by azredwing (Sexton)
on Jan 30, 2008 at 06:22 UTC ( [id://665066]=perlquestion: print w/replies, xml ) Need Help??

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

Hi all, I just learned Perl for my work and I've got a quick question if someone could help me. A program I wrote reads in a list of eight-bit hex numbers. The first four is a command, the last four are arguments to that command. My program generates a summary of which commands occurred and what arguments did they occur with. Simple enough:
Command 01a3 executed 6 times: 0213 0214 0215 0216 0217 0218
The thing is, some commands will run around 35 to 40 times, and to keep them all in one column is not exactly a great use of space considering these reports are printed and kept hard-copy. So, what I need is a way to create multiple columns (up to 4) based on how many entries there are in the array in which I keep those numbers. I wrote a fast solution to do this with duplicate commands/arguments as the entries, but it's not the most elegant (I begin teaching myself Perl about a month ago):
###this code only prints up to 3 columns, not 4 print OUT "\nThe following ", scalar @dupes, " tokens were duplicated: +\n"; if(@dupes<=10) { foreach (@dupes) { print OUT "\t$_\n"; } } elsif(@dupes<=20) #print two columns of dupes { for($i=0; $i<=10; $i++) { if(defined ($dupes[10+$i])) { $_=$dupes[$i]."\t".$dupes[10+$i]; } else { $_=$dupes[$i]; } print OUT "$_\n"; } } else #print three columns { no warnings; for($i=0; $i<=(@dupes/3); $i++) { if(defined $dupes[$i+(2/3)*@dupes]) { $_=$dupes[$i]."\t\t".$dupes[(1/3)*@dupes+$i+1]."\t\t".$dup +es[(2/3)*@dupes+$i+2]; } elsif(defined $dupes[$i+(1/3)*@dupes]) { $_=$dupes[$i]."\t\t".$dupes[(1/3)*@dupes+$i+1]; } else { $_=$dupes[$i]; } print OUT "\t$_\n"; } }
While I can probably use this code to implement multiple columns, I would probably screw something up when it came time to maintain it. It took forever to tweak that code as it was to make sure I didn't either accidentally reprint a line or skip a line. Any ideas? I'd be much obliged.

Replies are listed 'Best First'.
Re: Long array -> multiple columns?
by ikegami (Patriarch) on Jan 30, 2008 at 06:38 UTC

    A couple of general tips.

    • When modifying a global for local use (e.g. $_), localize it first.
    • I don't see $i declared. Is strict on?
    • Almost always, C-style loops are much harder to read and maintain then their Perl equivalents.
      Consider using for my $i (a..b) instead of for (my $i=a; $i<=b; $i++).
    • If you want to hide a warn, why not just hide *that* warning. e.g. no warnings 'uninitialized';
      $i was previously declared, I should have mentioned that. This is a snippet of the entire code. Like I said, I only just learned Perl. I learned C last semester and the stuff I do at my work is a lot of string-manipulation stuff so that's why I switched. I wrote the initial thing when I was still making the transition from C to Perl -- I'd basically write C code with Perl. I'm getting much better at coding Perl-style-Perl but it's been a month. I also did not know I could hide specific warnings. Thanks.

        The reason I mentioned $i is that you reuse the variable (not just the name). I typically view that as bad, especially when it's trivial to declare it for the loop itself.

        || vv for my $i (1..5) { ... }
Re: Long array -> multiple columns?
by ikegami (Patriarch) on Jan 30, 2008 at 07:00 UTC

    Here's an implementation with simpler calculations, with fewer special cases and which can easily be extended to four columns.

    It also fixes some bug in yours. Yours skips the 17th element ($dupes[16]) when you have 23 elements (@dupes == 23). I didn't track down the cause of the error.

    The idea is simple: Pretend the array is a 2d array, then combine the row and col indexes into one.

    my @dupes = qw( a b c d e f g h i j k l m n o p q r s t u v w ); my $num_cols = int( @dupes / 10 ) + 1; if ($num_cols > 3) { $num_cols = 3; } my $num_rows = int( $#dupes / $num_cols ) + 1; for my $r ( 0 .. $num_rows-1 ) { for my $c ( 0 .. $num_cols-1 ) { my $i = $r + $c * $num_rows; last if $i > $#dupes; print("\t$dupes[$i]"); } print("\n"); }

    Update: Added "The idea ..." paragraph.

      This looks perfect. Thanks so much for your help!
Re: Long array -> multiple columns?
by moritz (Cardinal) on Jan 30, 2008 at 06:48 UTC
    Your code is much more complicated than it need to be.

    It helps to write a function that prints the array in a defined number of columns:

    sub print_in_n_cols { my ($count, @items) = @_; while (@items){ for (1 .. $count){ print "\t", shift @items; last unless @items; } print "\n"; } }

    Now you can call that, based on the number of dupes:

    if (@dupes <= 10){ print_in_n_cols(1, @dupes); } elsif (@dupes <= 20){ print_in_n_cols(2, @dupes); } ...

    Actually I don't why you want this behaviour, it feels more natural to me to always print in four columns - but of course that's your choice.

        That's exactly the issue I had when I tried to approach it the first time by just popping/shifting stuff off like the subroutine above..
Re: Long array -> multiple columns?
by halley (Prior) on Jan 30, 2008 at 15:21 UTC
    There is a common Un*x tool called column that can do this job, and if you're familiar with the Un*x directory listing command ls, then you're probably familiar with the multiple-column output that it produces.

    I just looked at the C source code to this utility, and it's an ugly mess. Small and efficient, possibly, but not particularly literate. It looks like someone thought that a robo-indenting tool made code readable, because everything is indented consistently but there's nary a comment to explain how it works.

    This is something I thought I had seen on CPAN, but I guess Text::Table is a bit different in purpose.

    --
    [ e d @ h a l l e y . c c ]

      One advantage of ls's method (and presumably column's) is that it resizes the columns to fit what will be placed in them and takes into account the width of the terminal.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://665066]
Approved by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-19 01:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found