Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

Fun with two-dimensional arrays

by FoxtrotUniform (Prior)
on Aug 09, 2001 at 20:43 UTC ( #103487=perlquestion: print w/replies, xml ) Need Help??

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

So I'm the resident Perl Answer Guy at my orkplace (not that I'm the best Perl hacker here, but those better than I are either busy doing other stuff, like running the network, or just don't like answering questions). I got an interesting one the other day, and it's still bugging me.

My colleague had a 2d array simulating a textmode screen. 22 rows, 80 columns; however, he wanted to write to the "screen" first, then worry about over-long lines, so each row could have more than 80 chars in it. He had it set up like so:


The question was, how do you traverse this array row by row? The obvious answer,

foreach my $x (@vscr) { foreach my $y (@$x) { &do_stuff($y); } }
traverses the screen in the wrong order.

My first suggestion was to switch the order of x and y in the array; this isn't quite as intuitive for accesses, but makes the obvious traversal DTRT. But this bugged me: after all, TMTOWTDI, right? So I came up with this:

foreach my $row (0..21) { foreach my $col (@vscr) { &do_stuff($col[$row]); } }
(At this point, he has changed the order of x and y in the array, so this code never got used, which means it's untested and probably broken.)

This isn't so much worse than the obvious nested foreach approach, but it bugs me that the number of rows is hard coded. Is there a more general way to do this that's still fairly clean?

Replies are listed 'Best First'.
Re: Fun with two-dimensional arrays
by suaveant (Parson) on Aug 09, 2001 at 21:00 UTC
    I would say do it the C way... (ish)
    for my $x (0..21) { for my $y (0..79) { do_stuff($vscr[$x][$y]); } }
    It works, and you always know your coordinates... you might want to define 21 and 79 as variables at the top of the program, though... in case screen size changes... or do
    for my $x (0..$#vscr) { for my $y (0..$#{$vscr[$x]}) { do_stuff($vscr[$x][$y]); } }

                    - Ant
                    - Some of my best work - Fish Dinner

      The problem with this is that you might well have more than 80 columns in any given row, as text gets written to that row (but not yet wrapped; text wrapping or trucation gets done later on).

        Well, the second method accounts for that... the 0..$#{$vscr[$x]} gives you 0 thru the length of that row...

        or you could set the width of a row to be arbitrarily long... like 500 chars... and just keep reading till one of them was undef (assuming good coords had a space instead of undef...

                        - Ant
                        - Some of my best work - Fish Dinner

Re: Fun with two-dimensional arrays
by bikeNomad (Priest) on Aug 09, 2001 at 21:04 UTC
    I don't know why you say the number of rows is hard coded. Why can't you just do this:

    foreach my $row (0 .. $lastRow) { }

    But you might consider not storing it as a 2-dimensional array, but rather as a 22x80 character-long string. Then traversal becomes simply a matter of using substr and multiplying indexes (and it duplicates the behavior of wrapping that most terminals exhibit):

    my $screen = ' ' x ($rows * $columns); foreach my $row (0 .. $rows-1) { my $rowText = substr($screen, $row*$columns, $columns); } # sets text, returns old text. sub setTextAt { my $x = shift; my $y = shift; my $text = join('', @_); substr($screen, $y * $columns + $x, length($text), $text); }

      I guess I obscured my question with too much detail about the problem. I'm asking mostly to satisfy my curiosity.

      Without all the

      baggage, my question is: given an array of arrays (of arbitrary dimensions), how do you hit every element, first visiting the first element in each array, then the second, and so on?

      For instance, if you had:

      my $array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
      you'd want to visit in the order 1, 4, 7, 2, 5, 8, 3, 6, 9.

      Did that make sense?

      update: why @ (in scalar context) and $# didn't occur to me when I posted this, I don't know. That said, mapcar is damn cool.
        Why not cycle through the top-level array, shifting off the bottom element in each sub-array? Something like
        @array = ([1,2,3],[4,5,6],[7,8,9]); while (grep scalar @{ $_ }, @array) { for $i (@array) { print shift @{ $i }; } }
        This has the merit that not only do you not need to code (or even initialise, or know) the number and length of sub-arrays, but they can also be of different lengths.

        George Sherston
        Assuming you have fixed-size subarrays, you can do this:

        my $columns = 3; foreach my $x (0 .. $columns - 1) { foreach my $y (0 .. $#array) { print $array->[$y]->[$x] } }
        update: went back and re-read question.
(tye)Re: Fun with two-dimensional arrays
by tye (Sage) on Aug 10, 2001 at 01:49 UTC
    use mapcar; mapcar { do_stuff($_) for @_ } @$vscr;

    See mapcar.

            - tye (but my friends call me "Tye")
Re: Fun with two-dimensional arrays
by arturo (Vicar) on Aug 09, 2001 at 21:01 UTC

    No need to hardcode the values. You could set variables (or constants) to hold them, and put those in there. Or if you want to get even more "dynamic", you can use $#array to get the highest index of @array ($#$arrayref, btw =).

    I forget which module you can use to determine these values ahead of time.

    Now, I'm not *certain* of what you mean by "wrong order", but you can always do

    foreach my $x (reverse @array) { #...}

    as well. HTH!

    update Oh, I get it now. You want to process row-by-row, you get column-by-column. Sorry bout 'dat.

    perl -e 'print "How sweet does a rose smell? "; chomp ($n = <STDIN>); +$rose = "smells sweet to degree $n"; *other_name = *rose; print "$oth +er_name\n"'

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (2)
As of 2023-02-03 08:03 GMT
Find Nodes?
    Voting Booth?
    I prefer not to run the latest version of Perl because:

    Results (24 votes). Check out past polls.