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

I am currently looking into using structures in a program I am working on. For readability, I was wanting to use structures. Now I found the Class::Struct module and was using that, but then I found that Perl 5 Version 12.2 has Elaborate Records which are very similar to structures from what I can see. Now to build my skills with Perl, I am investigating how to use references, and references with arrays in these elaborate records.

The Problem:

I would like to learn how to retrieve the value for a reference to a scalar from an elaborate record and how to work with references to elaborate records.
my $stg_username = "PerlMonk"; my $stg_password = "VeryGoodPassword"; my @ary_email_address = [ "Monk1", "Monk2", "Monk3" ]; $EllabRecord = { m_username => \$stg_username , m_password => \$stg_password , m_emailAddress => \[@ary_email_address] }; print $stg_username; print $EllabRecord->{m_password}; #prints out "SCALAR (0xAABBCC)" &PrintArray( \$EllabRecord ); sub PrintArray { $reference = $_[0]; print $$reference->[m_emailAddress]->[0], "\n"; #gives a "Not an A +RRAY reference at...Line XX" }
If you can either provide me with clear code examples or a good guide to how this is done, either would be greatly appreciated. Thanking you in advance.

Replies are listed 'Best First'.
Re: Elaborate Records, arrays and references
by ikegami (Patriarch) on Jan 06, 2011 at 02:24 UTC

    The first error would have been caught by use strict;. Don't hide errors; use use strict; use warnings;.

    $$reference->[m_emailAddress]

    should be

    $$reference->{m_emailAddress}

    Secondly, you treat $$reference->{m_emailAddress} as a reference to an (anonymous) array of addresses. It is really a reference to a reference to an (anonymous) array that contains one reference to an (anonymous) array of addresses.

    $$reference->{m_emailAddress}->[0]

    should be

    ${ $$reference->{m_emailAddress} }->[0]->[0]

    What's with references to scalars and the crazy stacking of references?

    use strict; use warnings; sub print_record { my ($rec) = @_; print("username: $rec->{m_username}\n"); print("password: $rec->{m_password}\n"); print("email: @{ $rec->{m_email_addresses} }\n"); } my $stg_username = "PerlMonk"; my $stg_password = "VeryGoodPassword"; my @stg_email_addresses = ( "Monk1", "Monk2", "Monk3" ); my $ellab_rec = { m_username => $stg_username, m_password => $stg_password, m_emailAddress => \@stg_email_addresses, }; print_record($ellb_rec);
      @Ikegami-san,

      I was looking at having ways of updating the variables on the fly, but reviewing my code passing the Elaborate Record as a parameter and localising it may work.

      The one thing I am trying to do is for one subroutine to pass a Elaborate Record as a parameter to a second subroutine, this second subroutine will update the Elaborate Record and then send it back to the 1st subroutine. The two subroutines are most likely going to be in different packages so just trying to work out how to best do this.

      Thanks for your reply and the help in understanding all this. I also really appreciate the final example aswell. Very helpful.

        You don't need all those levels of indirect to do what you want to do either.

        use strict; use warnings; sub change_record { my ($rec) = @_; $rec->{m_username} = uc($rec->{m_username}); } { my $stg_username = "PerlMonk"; my $stg_password = "VeryGoodPassword"; my @stg_email_addresses = ( "Monk1", "Monk2", "Monk3" ); my $rec = { m_username => $stg_username, m_password => $stg_password, m_emailAddress => \@stg_email_addresses, }; print("username: $rec->{m_username}\n"); change_record($rec); print("username: $rec->{m_username}\n"); }

        The $rec inside of change_record is a different variable than the one on the outside (two "my"), but they both hold a reference to the same hash.

Re: Elaborate Records, arrays and references
by Marshall (Canon) on Jan 06, 2011 at 05:45 UTC
    The closest thing in Perl to the C "array of struct" is a Perl "Array of Hash".

    Just like in C, normally you want to make "deep copies" of each "element of the struct". In C, you could perhaps do a malloc of some space for m_username and then copy $stg_username into that allocated memory.

    In Perl this is easier. No malloc() is required. And no explicit copy is required.

    If you actually DO mean a reference to a string to be used in place of a copy of the string, then that is of course possible also. Most of the time, I use a deep copy instead of a reference to the string in Perl as it is so easy to do in Perl with just "=", malloc() and then strcpy()is not required.

    \[@ary_email_address] makes no sense. [ ary_email_address ] IS a reference to anon memory with email addresses. Another thing that is different than in C, is that each type of variable has its own name space. WOW! @a,$a,sub a,%a can all co-exist in the same program. So there is no need to prefix @email_address with @ary_email_address.

    I made a ArrayofHash (similar to C Array of Struct) below. In Perl, it is possible to find out whether a value is a "pointer" to an array or not and I demo'ed that below also. Also the Data::Dumper module is VERY handy - there is no such equivalent in C...also demo'd below.

    Hope this helps...

    #!/usr/bin/perl -w use strict; use Data::Dumper; my $username = "PerlMonk"; my $password = "VeryGoodPassword"; my @email_addresses = ( "Monk1", "Monk2", "Monk3" ); my %Record1 = ( username => $username, password => $password, emailAddress => [@email_addresses] ); my %Record2 = ( username => 'SomeOtherPerlMonk', password => 'AntoherVeryGoodPassword', emailAddress => ["monka", "monkb", "monkc"], ); my @AoH; push (@AoH, {%Record1}); #{%Record1} allocates new memory #for the anonymous hash, makes a copy, #of the whole hash table, then adds a #reference to that anon hash #to the array @AoH (similar to a C pointer). push (@AoH, {%Record2}); print Dumper \@AoH; $username = "XXXXXXXXXXXXXXXXXXXX"; # the record in AoH is unchanged because $username was copied # into that record #print Dumper \@AoH; #un-coment to see same thing again print "\nDumping records....\n"; foreach my $record (@AoH) { foreach my $key (keys %$record) { if ( ref ($record->{$key}) eq 'ARRAY' ) { print "$key => @{$record->{$key}}\n"; } else { print "$key => $record->{$key}\n"; } } print "\n"; } __END__ $VAR1 = [ { 'password' => 'VeryGoodPassword', 'emailAddress' => [ 'Monk1', 'Monk2', 'Monk3' ], 'username' => 'PerlMonk' }, { 'password' => 'AntoherVeryGoodPassword', 'emailAddress' => [ 'monka', 'monkb', 'monkc' ], 'username' => 'SomeOtherPerlMonk' } ]; Dumping records.... password => VeryGoodPassword emailAddress => Monk1 Monk2 Monk3 username => PerlMonk password => AntoherVeryGoodPassword emailAddress => monka monkb monkc username => SomeOtherPerlMonk
      I understand what ikegami and Marshall are saying but now I am seeking clarification on how it should be done in perl. In C what I would usually do is:
      typedef struct s_USERINFO{ CString m_username; CString m_password; CString m_emailAddress[20]; } USERINFO; int main () { USERINFO currentUserInfo; test( currentUserInfo ); printf ("%s", currentUserInfo.m_username); } void test ( USERINFO* thisUserInfo ) { thisUserInfo->m_username = "Pigsy's Perfect Ten"; }
      What I want to do is update the struct/hash in the subroutine/function and then be able to access the updated value in the main thread. In Perl my assumption on how to do this is:
      use warnings; use strict; use Data::Dumper; sub test (\%); #no strict 'refs'; my $username = "PerlMonk"; my $password = "VeryGoodPassword"; my @email_addresses = ( "Monk1", "Monk2", "Monk3" ); my %Record1 = ( username => '$' , password => $password , emailAddress => [@email_addresses] ); $Record1{username} = 'NOOoooooo!'; &test( %Record1 ); sub test (\%) { my $reference = shift; my $test = "Pigsy's Perfect Ten"; $$reference->{username} = $test; print $$reference->{username}, "\n"; } print $Record1{username}, "\n";
      So what I am hoping to have as an outcome of this is for the final print command to print out "Pigsy's Perfect Ten". I have two problems with this, I keep getting 'NOOoooooo!' printed out, and also I am getting the error "Can't use string ("password") as a HASH ref while "strict refs" in use at C:/xxx/test.pl line 24 ( &test( %Record1 );)." With both problems I am assuming there are problems with my syntax that I just can't figure out. Any help is appreciated.
        Just solved my own question. Below is the code i used, if you have another suggestion (especially with out using a variable like $hashref) I would like to hear it.
        use warnings; use strict; use Data::Dumper; sub test (\%); my %Record1 = ( username => '$' , password => '$' , emailAddress => '@' ); my $hashref = \%Record1; $hashref->{username} = 'NOOoooooo!'; &test( $hashref ); sub test (\%) { my $reference = shift; my $test = "Pigsy's Perfect Ten"; $reference->{username} = $test; print $reference->{username}, "\n"; } print $Record1{username}, "\n";
Re: Elaborate Records, arrays and references
by Anonymous Monk on Jan 06, 2011 at 06:00 UTC
    I want to note that perldsc is just the data-structures cookbook. It's just a way of using the types of variables and their references available in Perl 5 to create data structures. Nothing there is fundamentally new to perl 5.12. "More Elaborate Records" is a chapter title, not the name of a specific data structure or design pattern.

    "More-elaborate records" might have been a less confusing title, because it's really about data structures which are more elaborate than covered in that document up to that point. But the entire document is about data structures more elaborate than covered elsewhere, so the existing title, which parses as "more (elaborate records)", still works.

Re: Elaborate Records, arrays and references
by ikegami (Patriarch) on Jan 06, 2011 at 02:25 UTC

    The first error would have been caught by use strict;. Don't hide errors; use use strict; use warnings;.

    $$reference->[m_emailAddress]

    should be

    $$reference->{m_emailAddress}

    Secondly, you treat $$reference->{m_emailAddress} as a reference to an (anonymous) array of addresses. It is really a reference to a reference to an (anonymous) array that contains one reference to an (anonymous) array of addresses.

    $$reference->{m_emailAddress}->[0]

    should be

    ${ $$reference->{m_emailAddress} }->[0]->[0]

    What's with references to scalars and the crazy stacking of references?

    use strict; use warnings; sub print_record { my ($rec) = @_; print("username: $rec->{m_username}\n"); print("password: $rec->{m_password}\n"); print("email: @{ $rec->{m_email_addresses} }\n"); } my $stg_username = "PerlMonk"; my $stg_password = "VeryGoodPassword"; my @stg_email_addresses = ( "Monk1", "Monk2", "Monk3" ); my $ellab_rec = { m_username => $stg_username, m_password => $stg_password, m_emailAddress => \@stg_email_addresses, }; print_record($ellb_rec);