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

Hi there
I would like to do the following in perl:
Create a 2D matrix that gives an indication of how many people have two different animals in their home and what animals they are.
A 2D matrix - with the x axis labelled as 'cat', 'dog'. 'fish', 'rabbit'.
and also the y axis labelled 'cat', dog', 'fish, 'rabbit'.
and then - for example, if someone has a fish and a cat, the numeric counter in the square than corresponds to the hit 'fish' on one axis and 'cat' on the other goes up by one.
Any suggestions as how I might go about doing this would be much appreciated.
p.s. this is not a 'homework' question, more a general 'learning about perl' exercise.
  • Comment on help with 2D arrays with perl requested

Replies are listed 'Best First'.
Re: help with 2D arrays with perl requested
by roboticus (Chancellor) on Apr 29, 2006 at 12:28 UTC
    Anonymonk:

    I know you've specified a 2D matrix, but in this case, I think a hash would be simpler. Think of the hash as a sparse array. Something like this:

    #!/usr/bin/perl -w use warnings; use strict; my @animals = ("cat", "dog", "fish", "horse", "pig"); my %count; while (<DATA>) { my ($x,$y) = split /\s/; ($x,$y) = ($y,$x) if ($x gt $y); $count{$x}{$y}++; } for my $x (sort keys %count) { for my $y (sort keys %{$count{$x}}) { print $count{$x}{$y}," people have both a ", $x, " and a ", $y, "\n"; } } __DATA__ cat dog cat fish pig dog horse fish pig fish dog fish fish cat
    --roboticus
      ...and here's the 2D array method I tried. You can see why I prefer the hash, as it's a good bit more involved, since you need to map the animal names into array indices. Take out the lines that start with "next" and you'll see another reason I prefer the hash.

      And now for the code....

      #!/usr/bin/perl -w use warnings; use strict; my @animals = ("cat", "dog", "fish", "horse", "pig"); # Hash to map an animal to an array index. I'm sure there's a # better way, but I don't know it... my %a2i; my $i=0; for my $a (@animals) { $a2i{$a}=$i++; } my @count; while (<DATA>) { my ($x,$y) = split /\s/; ($x,$y) = ($y,$x) if ($x gt $y); $count[$a2i{$x}][$a2i{$y}]++; } for my $x (sort keys %a2i) { next if !defined $count[$a2i{$x}]; for my $y (sort keys %a2i) { next if !defined $count[$a2i{$x}][$a2i{$y}]; print $count[$a2i{$x}][$a2i{$y}], " people have both a ", $x, " and a ", $y, "\n"; } } __DATA__ cat dog cat fish pig dog horse fish pig fish dog fish fish cat
      --roboticus
        Wouldn't this accomplish the task more simply?
        #!/usr/bin/perl -w use warnings; use strict; my %count; while (<DATA>) { chomp; my ($x,$y) = split /\s/; ($x,$y) = ($y,$x) if ($x gt $y); $count{"$x $y"}++; } foreach (sort keys %count) { print "people with $_: $count{$_}\n"; } __DATA__ cat dog cat fish pig dog horse fish pig fish dog fish fish cat
        Thank you very much. I really appreciate your help

        just out of interest - in your opinion - how difficult might it be to convert the results into a graphical form? I was thinking something along the lines of a matrix with squares filled in at the appropriate places?

Re: help with 2D arrays with perl requested
by TedPride (Priest) on Apr 29, 2006 at 20:37 UTC
    The following is a quick hack that generates test data, processes it, and displays it in a grid form. The function table() is adapted from a post I made a day or two ago.
    use strict; use warnings; my (%pairs, %animals, %owns, @names, @animals, @owns, @display, $name, + $animal, $x, $y); @names = qw/Adrian Alannah Allison Angus Anna Ashton Aurelia Autumn/; @animals = qw/cockatoo crane crow dove duck egret emu flamingo goose/; for (1..($#names * $#animals / 2)) { $name = $names[int rand($#names + 1)]; $animal = $animals[int rand($#animals + 1)]; if (!$owns{$name}{$animal}) { $owns{$name}{$animal} = 1; push @owns, [$name, $animal]; } } @owns = sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @owns; print join "\n", map { "$_->[0] owns $_->[1]" } @owns; print "\n\n"; for (values %owns) { @owns = keys %$_; $animals{$_} = 1 for @owns; for $x (0..($#owns-1)) { for $y (($x+1)..$#owns) { $pairs{"$owns[$x] $owns[$y]"}++; $pairs{"$owns[$y] $owns[$x]"}++; } } } @animals = sort keys %animals; push @display, ['', @animals]; for $y (@animals) { push @display, [$y, map { $pairs{"$y $_"} ? $pairs{"$y $_"} : '' } + @animals]; } table(' | ', \@display); sub table { my ($separator, $arr) = @_; my ($i, @lengths, $length, $format); for (@$arr) { for $i (0..$#$_) { $lengths[$i] = length($_->[$i]) if !$lengths[$i] || $lengt +hs[$i] < length($_->[$i]); } } $length = 0; $length += $_ for @lengths; $length += length($separator) * $#lengths + 4; $format = join $separator, map { '%-'.$_.'s' } @lengths; print '-' x $length, "\n"; no warnings; print '| ', sprintf($format, @$_), ' |', "\n", '-' x $length, "\n" for @$arr; }