help on hash of hashes

Discussion in 'Perl Misc' started by Stephen Moon, Feb 20, 2004.

  1. Stephen Moon

    Stephen Moon Guest

    Hi,

    Can you help me with the below error? Thanks in advance.

    -Steve



    input file:
    ====================================================
    flintstones: lead=fred pal=barney
    jetsons: lead=george wife=jane boy=elroy
    simpsons: lead=homer wife=marge kid=bart

    perl source code:
    ====================================================

    #!/usr/bin/perl -w

    use strict;

    my %HoH; #hash table for the data structure
    my $rfwho;
    if($#ARGV != 1) {
    die("Invalid number of arguments\nUsage: perl test.pl <input_file>
    <output_file>\n");
    }

    my $infile = $ARGV[0];
    my $outfile = $ARGV[1];

    open(DATA_IN,"$infile") || die("could not open $infile\n");
    open(DATA_OUT,">$outfile") || die("could not open $outfile\n");

    while ( my $line = <DATA_IN> ) {
    chomp($line);
    if($line =~ /^(.*?):\s*/){
    my $who = $1;
    $rfwho = \$who;
    my $rfrec = {};
    $HoH{$who} = $rfrec;
    for my $field ( split /\s+/, $line) {
    my ($key, $value) = split /=/,$field;
    $rfrec->{$key} = $value;
    }
    }
    }

    foreach my $family ( keys %HoH ) {
    print "$family: ";
    foreach my $role ( keys %{ $HoH{$family} } ) {
    printf(DATA_OUT "$role=$HoH{$family}{$role}"); <= error here
    }
    printf(DATA_OUT "\n");
    }

    output error:
    ====================================================

    Use of uninitialized value in concatenation (.) or string at test.pl
    line 34, <DATA_IN> line 3.
    Use of uninitialized value in concatenation (.) or string at test.pl
    line 34, <DATA_IN> line 3.
    Use of uninitialized value in concatenation (.) or string at test.pl
    line 34, <DATA_IN> line 3.
     
    Stephen Moon, Feb 20, 2004
    #1
    1. Advertising

  2. Stephen Moon

    Paul Lalli Guest

    On Fri, 20 Feb 2004, Stephen Moon wrote:

    > Hi,
    >
    > Can you help me with the below error? Thanks in advance.
    >
    > -Steve
    >
    >
    >
    > input file:
    > ====================================================
    > flintstones: lead=fred pal=barney
    > jetsons: lead=george wife=jane boy=elroy
    > simpsons: lead=homer wife=marge kid=bart
    >
    > perl source code:
    > ====================================================
    >
    > #!/usr/bin/perl -w
    >
    > use strict;
    >
    > my %HoH; #hash table for the data structure
    > my $rfwho;
    > if($#ARGV != 1) {
    > die("Invalid number of arguments\nUsage: perl test.pl <input_file>
    > <output_file>\n");
    > }
    >
    > my $infile = $ARGV[0];
    > my $outfile = $ARGV[1];
    >
    > open(DATA_IN,"$infile") || die("could not open $infile\n");
    > open(DATA_OUT,">$outfile") || die("could not open $outfile\n");
    >
    > while ( my $line = <DATA_IN> ) {
    > chomp($line);
    > if($line =~ /^(.*?):\s*/){
    > my $who = $1;
    > $rfwho = \$who;
    > my $rfrec = {};
    > $HoH{$who} = $rfrec;
    > for my $field ( split /\s+/, $line) {

    ^^^^^^

    $line still contains the entire line, including (for example)
    "flintsones:". THerefore, "flinstones:" becomes one of your fields

    > my ($key, $value) = split /=/,$field;


    There is no = found in "flinstones:", so $key gets that whole string, and
    $value is left as undef.



    > $rfrec->{$key} = $value;


    You've just assigned a new position in the hash to be undef.

    > }
    > }
    > }
    >
    > foreach my $family ( keys %HoH ) {
    > print "$family: ";
    > foreach my $role ( keys %{ $HoH{$family} } ) {
    > printf(DATA_OUT "$role=$HoH{$family}{$role}"); <= error here



    Now you try to print out that undef value, and Perl correctly warns you.


    Hope this helps,
    Paul Lalli
     
    Paul Lalli, Feb 20, 2004
    #2
    1. Advertising

  3. Stephen Moon

    Anno Siegel Guest

    Stephen Moon <> wrote in comp.lang.perl.misc:
    > Hi,
    >
    > Can you help me with the below error? Thanks in advance.


    [lots of code snipped]

    Do not dump your problem as is to the newsgroup, make an attempt to
    solve it yourself first.

    The code I snipped contained things like checking the number of parameters
    in @ARGV. Why should hundreds of people have to read that code,
    understand what it does, and ignore it? It's your job to do that.

    > foreach my $family ( keys %HoH ) {
    > print "$family: ";
    > foreach my $role ( keys %{ $HoH{$family} } ) {
    > printf(DATA_OUT "$role=$HoH{$family}{$role}"); <= error here


    Don't say "printf" when you mean "print". There's a difference, and
    some day it will bite you. Also, I suppose you want to print a line
    feed inside the loop too.

    > }
    > printf(DATA_OUT "\n");
    > }
    >
    > output error:
    > ====================================================
    >
    > Use of uninitialized value in concatenation (.) or string at test.pl
    > line 34, <DATA_IN> line 3.


    That's not an error but a warning, so the program should have printed
    something to the output file. It would be interesting to know what
    it was, but you didn't supply it.

    In any case, you have not exhausted your own means by a long shot.

    The warning tells you there's an undefined value in a string, so
    at least one of "$role" or "$hoh{$family}{$role}" is undefined.

    To find out which, change the code to tell you:

    foreach my $family ( keys %HoH ) {
    print "$family: ";
    foreach my $role ( keys %{ $HoH{$family} } ) {
    die '$role' unless defined $role;
    die '$HoH{$family}{$role}' unless defined $HoH{$family}{$role};
    print "$role=$HoH{$family}{$role}";
    }
    print "\n";
    }

    I bet you'll find that "$role" is defined but "$HoH{$family}{$role}" isn't.

    Ask yourself why you think it should have a value. Then go back through
    your code and verify that it does what you think it does.

    In other words, learn to debug a program. It's a skill as necessary as
    writing it in the first place.

    Anno
     
    Anno Siegel, Feb 20, 2004
    #3
  4. Stephen Moon

    Stephen Moon Guest

    -berlin.de (Anno Siegel) wrote in message news:<c15lb1$l05$-Berlin.DE>...
    > Stephen Moon <> wrote in comp.lang.perl.misc:
    > > Hi,
    > >
    > > Can you help me with the below error? Thanks in advance.

    >
    > [lots of code snipped]
    >
    > Do not dump your problem as is to the newsgroup, make an attempt to
    > solve it yourself first.
    >
    > The code I snipped contained things like checking the number of parameters
    > in @ARGV. Why should hundreds of people have to read that code,
    > understand what it does, and ignore it? It's your job to do that.
    >
    > > foreach my $family ( keys %HoH ) {
    > > print "$family: ";
    > > foreach my $role ( keys %{ $HoH{$family} } ) {
    > > printf(DATA_OUT "$role=$HoH{$family}{$role}"); <= error here

    >
    > Don't say "printf" when you mean "print". There's a difference, and
    > some day it will bite you. Also, I suppose you want to print a line
    > feed inside the loop too.
    >
    > > }
    > > printf(DATA_OUT "\n");
    > > }
    > >
    > > output error:
    > > ====================================================
    > >
    > > Use of uninitialized value in concatenation (.) or string at test.pl
    > > line 34, <DATA_IN> line 3.

    >
    > That's not an error but a warning, so the program should have printed
    > something to the output file. It would be interesting to know what
    > it was, but you didn't supply it.
    >
    > In any case, you have not exhausted your own means by a long shot.
    >
    > The warning tells you there's an undefined value in a string, so
    > at least one of "$role" or "$hoh{$family}{$role}" is undefined.
    >
    > To find out which, change the code to tell you:
    >
    > foreach my $family ( keys %HoH ) {
    > print "$family: ";
    > foreach my $role ( keys %{ $HoH{$family} } ) {
    > die '$role' unless defined $role;
    > die '$HoH{$family}{$role}' unless defined $HoH{$family}{$role};
    > print "$role=$HoH{$family}{$role}";
    > }
    > print "\n";
    > }
    >
    > I bet you'll find that "$role" is defined but "$HoH{$family}{$role}" isn't.
    >
    > Ask yourself why you think it should have a value. Then go back through
    > your code and verify that it does what you think it does.
    >
    > In other words, learn to debug a program. It's a skill as necessary as
    > writing it in the first place.
    >
    > Anno


    Thanks for all your help.

    Well, the above code is not mine. It's actually taken out from the
    Programming Perl Third Edition book (i.e. Camel book). I was trying
    to figure out how to generate a hash of hashes and how to print them
    out by trying out his examples, but somehow I couldn't not get it to
    work.

    -Steve
     
    Stephen Moon, Feb 21, 2004
    #4
  5. Stephen Moon

    Stephen Moon Guest

    Have another question. I fixed the code as you told me to

    while ( my $line = <DATA_IN> ) {
    chomp($line);
    if($line =~ s/^(.*?):\s*//){ <===fixed here
    my $who = $1;
    $rfwho = \$who;
    my $rfrec = {};
    $HoH{$who} = $rfrec;
    for my $field ( split /\s+/, $line) {
    my ($key, $value) = split /=/,$field;
    $rfrec->{$key} = $value;
    }
    }
    }

    foreach my $family ( sort keys %HoH ) {
    printf(DATA_OUT "$family:\n");
    foreach my $role ( sort keys %{ $HoH{$family} } ) {
    printf(DATA_OUT "$role=$HoH{$family}{$role}\n");
    }
    }

    The output that I get is below:

    flintstones:
    lead=fred
    pal=barney
    jetsons:
    boy=elroy
    lead=george
    wife=jane
    simpsons:
    kid=bart
    lead=homer
    wife=marge

    How can I change the above code so that I can output as below?

    flintstones:,jetsons:,simpsons:
    lead=fred,boy=elroy,kid=bart
    pal=barney,lead=george,lead=homer
    ,wife=jane,wife=marge

    Is this even possible? I have been struggling with this one a bit.

    Thanks in advance.

    -Steve
     
    Stephen Moon, Feb 21, 2004
    #5
  6. Stephen Moon

    Anno Siegel Guest

    Stephen Moon <> wrote in comp.lang.perl.misc:
    [in reply to a posting of mine (Anno)]

    > Have another question. I fixed the code as you told me to


    Please show a little of the context you are replying to. It seems to
    me that you are really replying to Paul Lalli. You have incorporated
    some of his advice, it appears, but partly ignored mine.

    You did reduce the code to the essentials, and that makes it much more
    usenet-friendly. Now if it also used the DATA filehandle for input, and
    STDOUT for output, instead of external files, it would be an ideal
    example of code you can copy/paste into an editor and run it. Well,
    there is still an unused and undeclared variable. The code should run
    under strict and warnings.

    > while ( my $line = <DATA_IN> ) {
    > chomp($line);
    > if($line =~ s/^(.*?):\s*//){ <===fixed here
    > my $who = $1;
    > $rfwho = \$who;


    Unused, undeclared. Why is it here?

    > my $rfrec = {};
    > $HoH{$who} = $rfrec;
    > for my $field ( split /\s+/, $line) {
    > my ($key, $value) = split /=/,$field;
    > $rfrec->{$key} = $value;
    > }
    > }
    > }
    >
    > foreach my $family ( sort keys %HoH ) {
    > printf(DATA_OUT "$family:\n");
    > foreach my $role ( sort keys %{ $HoH{$family} } ) {


    It doesn't make much sense to sort the roles, since they are largely
    independent in each family. If there were a set of roles that must
    be present in every family it would make sense to place these first
    and sort them, but in general it doesn't.

    > printf(DATA_OUT "$role=$HoH{$family}{$role}\n");


    Still using printf without a format. I told you it's going to bite.

    > }
    > }
    >
    > The output that I get is below:
    >
    > flintstones:
    > lead=fred
    > pal=barney
    > jetsons:
    > boy=elroy
    > lead=george
    > wife=jane
    > simpsons:
    > kid=bart
    > lead=homer
    > wife=marge
    >
    > How can I change the above code so that I can output as below?


    To see the relation of what you have to what you want more clearly,
    print one line for each family. I think your original code tried to
    do that, but it didn't seem to make sense then. It does now. So,
    instead of your second loop, do this:

    foreach my $family ( sort keys %HoH ) {
    my $line = "$family: ";
    foreach my $role ( keys %{ $HoH{$family} } ) {
    $line .= "$role=$HoH{$family}{$role} ";
    }
    print "$line\n"; # not printf!
    }

    This prints

    flintstones: pal=barney lead=fred
    jetsons: boy=elroy lead=george wife=jane
    simpsons: kid=bart lead=homer wife=marge

    while you want

    > flintstones:,jetsons:,simpsons:
    > lead=fred,boy=elroy,kid=bart
    > pal=barney,lead=george,lead=homer
    > ,wife=jane,wife=marge


    Looking at it and squinting a little we see that the columns of the first
    one make up the lines of the second. So the problem turns out to be
    one of matrix transposition.

    > Is this even possible? I have been struggling with this one a bit.


    It's certainly possible, I'll sketch a solution. You may want to try and
    come up with your own method, the one I'm using is a little advanced.

    It also ignores the HoH representing the families. Looking at the
    line-wise output again, it appears that it essentially reproduces the
    input lines. So to arrive at the wanted output, we can start directly
    from the input without building the HoH structure.

    We build an array of arrays, splitting the input lines on white space,
    then transpose that. The first line of the resulting array of arrays
    contains the families, the following lines contain the various roles.
    We never bothered to separate the role specifications ("lead", "wife"...)
    from the associated names. We print the families and the role lines in
    slightly different formats. The result is:

    flintstones: jetsons: simpsons:
    lead=fred, lead=george, lead=homer
    pal=barney, wife=jane, wife=marge
    , boy=elroy, kid=bart

    The table is differently arranged from your example, but it contains
    the same information.

    Anno

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

    my @columns = transpose( map [ split], <DATA>);

    print "@$_\n" for shift @columns; # print header
    foreach ( @columns ) {
    print join( ', ', @$_), "\n"; # print role columns
    }

    sub transpose {
    my $max = 0;
    $max > @$_ or $max = @$_ for @_; # length of longest line
    map [ map shift( @$_) || '', @_], 1 .. $max;
    }

    __DATA__
    flintstones: lead=fred pal=barney
    jetsons: lead=george wife=jane boy=elroy
    simpsons: lead=homer wife=marge kid=bart
     
    Anno Siegel, Feb 21, 2004
    #6
  7. Stephen Moon

    Stephen Moon Guest

    -berlin.de (Anno Siegel) wrote in message news:<c18ang$8t9$-Berlin.DE>...
    > Stephen Moon <> wrote in comp.lang.perl.misc:
    > [in reply to a posting of mine (Anno)]
    >
    > > Have another question. I fixed the code as you told me to

    >
    > Please show a little of the context you are replying to. It seems to
    > me that you are really replying to Paul Lalli. You have incorporated
    > some of his advice, it appears, but partly ignored mine.
    >
    > You did reduce the code to the essentials, and that makes it much more
    > usenet-friendly. Now if it also used the DATA filehandle for input, and
    > STDOUT for output, instead of external files, it would be an ideal
    > example of code you can copy/paste into an editor and run it. Well,
    > there is still an unused and undeclared variable. The code should run
    > under strict and warnings.
    >


    You are pretty funny:) just kidding:) I did take your advice and the
    prior poster. I just realized that my mistake was making some changes
    to the original
    code without carefully taking a look at what it did. Your advice also
    helped me since it showed how I can debug my program. Much thanks to
    both of you.

    > > while ( my $line = <DATA_IN> ) {
    > > chomp($line);
    > > if($line =~ s/^(.*?):\s*//){ <===fixed here
    > > my $who = $1;
    > > $rfwho = \$who;

    >
    > Unused, undeclared. Why is it here?
    >


    well, I was trying to use the reference of $who in the following loop
    in different scope for some other thing and didn't come around fixing
    it.

    > > my $rfrec = {};
    > > $HoH{$who} = $rfrec;
    > > for my $field ( split /\s+/, $line) {
    > > my ($key, $value) = split /=/,$field;
    > > $rfrec->{$key} = $value;
    > > }
    > > }
    > > }
    > >
    > > foreach my $family ( sort keys %HoH ) {
    > > printf(DATA_OUT "$family:\n");
    > > foreach my $role ( sort keys %{ $HoH{$family} } ) {

    >
    > It doesn't make much sense to sort the roles, since they are largely
    > independent in each family. If there were a set of roles that must
    > be present in every family it would make sense to place these first
    > and sort them, but in general it doesn't.
    >
    > > printf(DATA_OUT "$role=$HoH{$family}{$role}\n");

    >
    > Still using printf without a format. I told you it's going to bite.
    >


    Well, I am a C programmer, so bear with me. I takes a little while to
    make a transition.

    > > }
    > > }
    > >
    > > The output that I get is below:
    > >
    > > flintstones:
    > > lead=fred
    > > pal=barney
    > > jetsons:
    > > boy=elroy
    > > lead=george
    > > wife=jane
    > > simpsons:
    > > kid=bart
    > > lead=homer
    > > wife=marge
    > >
    > > How can I change the above code so that I can output as below?

    >
    > To see the relation of what you have to what you want more clearly,
    > print one line for each family. I think your original code tried to
    > do that, but it didn't seem to make sense then. It does now. So,
    > instead of your second loop, do this:
    >
    > foreach my $family ( sort keys %HoH ) {
    > my $line = "$family: ";
    > foreach my $role ( keys %{ $HoH{$family} } ) {
    > $line .= "$role=$HoH{$family}{$role} ";
    > }
    > print "$line\n"; # not printf!
    > }
    >
    > This prints
    >
    > flintstones: pal=barney lead=fred
    > jetsons: boy=elroy lead=george wife=jane
    > simpsons: kid=bart lead=homer wife=marge
    >
    > while you want
    >
    > > flintstones:,jetsons:,simpsons:
    > > lead=fred,boy=elroy,kid=bart
    > > pal=barney,lead=george,lead=homer
    > > ,wife=jane,wife=marge

    >
    > Looking at it and squinting a little we see that the columns of the first
    > one make up the lines of the second. So the problem turns out to be
    > one of matrix transposition.
    >
    > > Is this even possible? I have been struggling with this one a bit.

    >
    > It's certainly possible, I'll sketch a solution. You may want to try and
    > come up with your own method, the one I'm using is a little advanced.
    >
    > It also ignores the HoH representing the families. Looking at the
    > line-wise output again, it appears that it essentially reproduces the
    > input lines. So to arrive at the wanted output, we can start directly
    > from the input without building the HoH structure.
    >
    > We build an array of arrays, splitting the input lines on white space,
    > then transpose that. The first line of the resulting array of arrays
    > contains the families, the following lines contain the various roles.
    > We never bothered to separate the role specifications ("lead", "wife"...)
    > from the associated names. We print the families and the role lines in
    > slightly different formats. The result is:
    >
    > flintstones: jetsons: simpsons:
    > lead=fred, lead=george, lead=homer
    > pal=barney, wife=jane, wife=marge
    > , boy=elroy, kid=bart
    >
    > The table is differently arranged from your example, but it contains
    > the same information.
    >
    > Anno
    >
    > --------------------------------------------------------
    > #!/usr/bin/perl
    > use strict; use warnings;
    >
    > my @columns = transpose( map [ split], <DATA>);
    >
    > print "@$_\n" for shift @columns; # print header
    > foreach ( @columns ) {
    > print join( ', ', @$_), "\n"; # print role columns
    > }
    >
    > sub transpose {
    > my $max = 0;
    > $max > @$_ or $max = @$_ for @_; # length of longest line
    > map [ map shift( @$_) || '', @_], 1 .. $max;
    > }
    >
    > __DATA__
    > flintstones: lead=fred pal=barney
    > jetsons: lead=george wife=jane boy=elroy
    > simpsons: lead=homer wife=marge kid=bart


    cool! I will check it out! Well, the reason that I use sort is
    because I will have frequency index associated with magnitudes.

    Thanks for your help.

    -Steve
     
    Stephen Moon, Feb 22, 2004
    #7
  8. Stephen Moon

    Ben Morrow Guest

    (Stephen Moon) wrote:
    > -berlin.de (Anno Siegel) wrote in message
    > news:<c18ang$8t9$-Berlin.DE>...
    > >
    > > Still using printf without a format. I told you it's going to bite.
    > >

    >
    > Well, I am a C programmer, so bear with me. I takes a little while to
    > make a transition.


    Even in C you should always printf("%s", string) rather than simply
    printf(string): otherwise you've got a security hole on your hands.
    What if you end up with string = "%d"?

    Ben

    --
    If you put all the prophets, | You'd have so much more reason
    Mystics and saints | Than ever was born
    In one room together, | Out of all of the conflicts of time.
    |----------------+---------------| The Levellers, 'Believers'
     
    Ben Morrow, Feb 22, 2004
    #8
  9. Stephen Moon

    Stephen Moon Guest

    Ben Morrow <> wrote in message news:<c1914h$du9$>...
    > (Stephen Moon) wrote:
    > > -berlin.de (Anno Siegel) wrote in message
    > > news:<c18ang$8t9$-Berlin.DE>...
    > > >
    > > > Still using printf without a format. I told you it's going to bite.
    > > >

    > >
    > > Well, I am a C programmer, so bear with me. I takes a little while to
    > > make a transition.

    >
    > Even in C you should always printf("%s", string) rather than simply
    > printf(string): otherwise you've got a security hole on your hands.
    > What if you end up with string = "%d"?
    >
    > Ben


    i c. Of course, that's what I would if i were to write a C program.
    I didn't understand what he meant by "using printf without a format."

    What is a difference between using "print" and "printf"? How would
    you use "printf" with a format? I am relatively new to perl. Maybe
    you can enlighten me. So, you are telling me that

    printf(DATA_OUT "$role=$HoH{$family}{$role}\n");

    should be written as

    printf(DATA_OUT "%s = %s\n", $role, $HoH{$family}{$role});

    -Steve
     
    Stephen Moon, Feb 22, 2004
    #9
  10. Stephen Moon

    gnari Guest

    "Stephen Moon" <> wrote in message
    news:...

    > What is a difference between using "print" and "printf"? How would
    > you use "printf" with a format? I am relatively new to perl. Maybe
    > you can enlighten me. So, you are telling me that
    >
    > printf(DATA_OUT "$role=$HoH{$family}{$role}\n");
    >
    > should be written as
    >
    > printf(DATA_OUT "%s = %s\n", $role, $HoH{$family}{$role});


    yes, or just
    print DATA_OUT "$role=$HoH{$family}{$role}\n";

    gnari
     
    gnari, Feb 22, 2004
    #10
  11. Stephen Moon

    Jay Tilton Guest

    (Stephen Moon) wrote:

    : I didn't understand what he meant by "using printf without a format."
    :
    : What is a difference between using "print" and "printf"?

    Consult perlfunc for explanations of each function's purpose.

    : How would
    : you use "printf" with a format? I am relatively new to perl. Maybe
    : you can enlighten me. So, you are telling me that
    :
    : printf(DATA_OUT "$role=$HoH{$family}{$role}\n");
    :
    : should be written as
    :
    : printf(DATA_OUT "%s = %s\n", $role, $HoH{$family}{$role});

    Yes, that is the appropriate printf() statement.

    More simply, change the printf() to an ordinary print().

    print( DATA_OUT "$role=$HoH{$family}{$role}\n" );
     
    Jay Tilton, Feb 22, 2004
    #11
  12. Stephen Moon

    Stephen Moon Guest

    Anno,

    > > --------------------------------------------------------
    > > #!/usr/bin/perl
    > > use strict; use warnings;
    > >
    > > my @columns = transpose( map [ split], <DATA>);
    > >
    > > print "@$_\n" for shift @columns; # print header
    > > foreach ( @columns ) {
    > > print join( ', ', @$_), "\n"; # print role columns
    > > }
    > >
    > > sub transpose {
    > > my $max = 0;
    > > $max > @$_ or $max = @$_ for @_; # length of longest line
    > > map [ map shift( @$_) || '', @_], 1 .. $max;
    > > }


    I have a hard time figuring out how transpose function. Maybe, a
    little explanation? Especially where you have "map [ map shift( @$_)
    || '',@_], 1 .. $max" Furthermore, what would be difference in using
    {} instead of [].

    -Steve
    > >
    > > __DATA__
    > > flintstones: lead=fred pal=barney
    > > jetsons: lead=george wife=jane boy=elroy
    > > simpsons: lead=homer wife=marge kid=bart

    >
    > cool! I will check it out! Well, the reason that I use sort is
    > because I will have frequency index associated with magnitudes.
    >
    > Thanks for your help.
    >
    > -Steve
     
    Stephen Moon, Feb 23, 2004
    #12
  13. Stephen Moon

    Anno Siegel Guest

    Stephen Moon <> wrote in comp.lang.perl.misc:

    > Anno,


    [...]

    > > > sub transpose {
    > > > my $max = 0;
    > > > $max > @$_ or $max = @$_ for @_; # length of longest line
    > > > map [ map shift( @$_) || '', @_], 1 .. $max;
    > > > }

    >
    > I have a hard time figuring out how transpose function. Maybe, a
    > little explanation? Especially where you have "map [ map shift( @$_)
    > || '',@_], 1 .. $max"


    Well, it is rather compressed, so let's make it more explicit. First let's
    expand the outer map and introduce meaningful variable names and some
    comments along the way. (I'll assume $max has been calculated as before.
    Since it's the length of the longest line it is also the number of lines
    in the transposed matrix).

    my @lines = @_; # an array of arrays which represents a matrix
    my @cols; # will become the transposed matrix
    # extract $max columns
    for ( 1 .. $max ) {
    push @cols, [ map shift @$_ || '', @lines]; # unchanged
    }
    # done, return result
    @cols;

    Now do the same with the inner map. We are using a fundamental identity
    here. If some expression EXPR returns a list there are two ways to
    get an array reference to that list. You can write

    $ref = [ EXPR ];

    or you can do

    my @array = LIST;
    $ref = \ @array;

    The original code uses the first way for each (anonymous) column. The
    expanded code uses the second with the named variable @col. The result
    is the same.

    my @lines = @_; # an array of arrays which represents a matrix
    my @cols; # will become the transposed matrix
    # extract $max columns
    for ( 1 .. $max ) {
    # extract one column
    my @col;
    # walk through all lines
    for my $line ( @lines ) {
    # shave off one element of $line and add it to the current
    # column. if there are no more elements left in the line, add
    # an empty string. $line is destroyed in the process.
    push @col, shift @$line || '';
    }
    # another column is ready, push it onto the result list
    push @cols, \ @col;
    }
    # done, return result
    @cols;

    I hope that makes things clearer.

    As presented, this transposition routine isn't quite a general-purpose
    matrix transposition. For one, it's destructive. After using it on
    a matrix, the matrix lines will be reduced to empty lists. A general-
    purpose routine shouldn't do that. At least, a property like that must
    be documented. In blinking red, with a howling background noise.

    Secondly, it replaces undefined elements in the original matrix with
    empty strings. (Sloppily, it would even replace a 0 element with an
    empty string.) A general purpose routine has no business doing that,
    but in the case at hand it was convenient.

    > Furthermore, what would be difference in using
    > {} instead of [].


    "{}" produces a hashref from a list of pairs. "[]" produces an
    arrayref from any list. The two are never[1] interchangeable.

    Anno.

    [1] Huh. I said "never" about a Perl construct. Someone is going to
    come up with a counter-example.
     
    Anno Siegel, Feb 23, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. rp
    Replies:
    1
    Views:
    562
    red floyd
    Nov 10, 2011
  2. Scott  Gilpin
    Replies:
    2
    Views:
    232
  3. Perl Learner

    Hashes of hashes or just one hash ?

    Perl Learner, Jun 8, 2005, in forum: Perl Misc
    Replies:
    11
    Views:
    226
  4. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    230
  5. Replies:
    3
    Views:
    227
Loading...

Share This Page