slugman
slugman

Reputation: 33

Perl: correctly print array of arrays (dereference)

Hey fellow perl monks,

I'm still wrapping my head around how to correctly dereference. (I read the similar posts prior to posting, but unfortunately am still a bit cloudy on the concept.)

I have the following array, which internally is composed of two arrays. (BTW, I am using strict and warning pragmas.)

use strict; use warnings;
my @a1; my @a2;

where:

@a1 = ( "1MB", "2MB", ... )

and..

@a2 = ( "/home", "/home/debug", ... )

Both @a1 & @a2 are arrays which contain 51 rows. So, I populate these into my 2nd array.

my @b;
push (@b, [ @a1, @a2 ]);

However, when I try to print the results of @b:

sub newl { print "\n"; print "\n"; }
my $an1; my @an1;
$an1 = $#a1;
@an1 = ( 0, 1..$an1 );

for my $i (@an1) { print @b[$i]; &newl; }

I see references to the arrays:

ARRAY(0x81c0a10)
  .
ARRAY(0x81c0a50)
  .
  .
  .

How do I properly print this array? I know I need to dereference the array, I'm not sure how to go about doing this. I tried populating my array as such:

push (@b, [ \@a1, \@a2 ]);

Which produces the same results. I also tried:

for my $i (@an1) { print @{$b[$i]}; &newl; }

Which unfortunately errors due to having 0 as an array reference?

Can't use string ("0") as an ARRAY ref while "strict refs" in use at p_disk_ex6.pl line 42.

Any suggestions are greatly appreciated!

Upvotes: 0

Views: 2968

Answers (3)

Mark Nodine
Mark Nodine

Reputation: 163

If you want a general solution that doesn't assume how many elements there are in each of the sub-arrays, and which also allows arbitrary levels of nesting, you're better off using packages that someone else has already written for displaying recursive data structures. A particularly prevalent one is YAML, which you can install if you don't already have it by running cpan:

$ cpan
Terminal does not support AddHistory.

cpan shell -- CPAN exploration and modules installation (v1.9800)
Enter 'h' for help.

cpan[1]> install YAML

Then you can display arbitrary data structures easily. To demonstrate with a simple example:

use YAML;

my @a1 = qw(1MB 2MB 10MB 7MB);
my @a2 = qw(/foo /bar /flub /blub);

my @b = (\@a1, \@a2);

print Dump(\@b);

results in the output

---
-
  - 1MB
  - 2MB
  - 10MB
  - 7MB
-
  - /foo
  - /bar
  - /flub
  - /blub

For a slightly more complicated example

my @b = (\@a1, \@a2,
         { a => 0, b => 1 } );

gives

---
-
  - 1MB
  - 2MB
  - 10MB
  - 7MB
-
  - /foo
  - /bar
  - /flub
  - /blub
- a: 0
  b: 1

To read this, the three "-" characters in column 1 indicate an array with three elements. The first two elements have four sub elements each (the lines with "-" in column 3). The third outer element is a hash reference, since it is made up of "key: value" pairs.

A nice feature about YAML is that you can use it to dump any recursive data structure into a file, except those with subroutine references, and then read it back later using Load.

If you really have to roll your own display routine, that is certainly possible, but you'll have a much easier time if you write it recursively. You can check whether your argument is an array reference or a hash reference (or a scalar reference) by using ref:

my @a1 = qw(1MB 2MB 10MB 7MB);
my @a2 = qw(/foo /bar /flub /blub);

my @b = (\@a1, \@a2,
         { a => 0, b => 1 } );

print_recursive(\@b);
print "\n";

sub print_recursive {
    my ($obj) = @_;

    if (ref($obj) eq 'ARRAY') {
        print "[ ";
        for (my $i=0; $i < @$obj; $i++) {
            print_recursive($obj->[$i]);
            print ", " if $i < $#$obj;
        }
        print " ]";
    }
    elsif (ref($obj) eq 'HASH') {
        print "{ ";
        my @keys = sort keys %$obj;
        for (my $i=0; $i < @keys; $i++) {
            print "$keys[$i] => ";
            print_recursive($obj->{$keys[$i]});
            print ", " if $i < $#keys;
        }
        print " }";
    }
    else {
        print $obj;
    }
}       

which produces the output

[ [ 1MB, 2MB, 10MB, 7MB ], [ /foo, /bar, /flub, /blub ], { a => 0, b => 1 } ]

I have not written my example code to worry about pretty-printing, nor does it handle scalar, subroutine, or blessed object references, but it should give you the idea of how you can write a fairly general recursive data structure dumper.

Upvotes: 0

kvivek
kvivek

Reputation: 3491

Even Simply this also works

use strict;
use warnings;

my @a1 = qw(1MB 2MB 10MB 7MB);
my @a2 = qw(/foo /bar /flub /blub);

my @b = (@a1, @a2);
print "@b";

Upvotes: 0

roland.minner
roland.minner

Reputation: 85

A short example program, which might help you:

use strict;
use warnings;

my @a1 = qw(1MB 2MB 10MB 7MB);
my @a2 = qw(/foo /bar /flub /blub);

my @b = (\@a1, \@a2);
# equivalent long version:
# my @b = ();
# $b[0] = \@a1;
# $b[1] = \@a2;

for (my $i = 0; $i <= $#a2; $i++) {
    print "a1[$i]: $b[0][$i]\n";
    print "a2[$i]: $b[1][$i]\n";
    print "\n";
}

In your example you were pushin an anoymous arrayref [] into @b. Therefore $b[0] contained the arrayref.

my @b;
push (@b, [ \@a1, \@a2 ]);
# this corresponds to:
# $b[0][0] = \@a1;
# $b[0][1] = \@a2;

In the example where you wrote [@a1, @a2] you were creating an array_ref which contained the joined arrays @a1 and @a2 (first all elements of @a1, and then all elements of @a2):

my @b;
push(@b , [@a1, @a2]);
# $b[0] = ['1MB' , '2MB', '10Mb', '7MB', '/foo', '/bar', '/flub', '/blub']

Upvotes: 1

Related Questions