http://qs321.pair.com?node_id=128942

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

I'm reading in a text file, and I'm parsing each line into a unique structure. I've found that I can't dynamically allocated new variables if I use strict since it seems using strict means that all variables have to explicitly defined in main. Is this true? Is there an elegant to dynamically create structures (or any variables)?

Replies are listed 'Best First'.
Re: Dynamically allocating variables
by Fastolfe (Vicar) on Dec 02, 2001 at 05:26 UTC

    What you're describing, symbolic variable references, is generally discouraged. You can probably get away with using a hash instead.

    my %data; while (<DATA>) { chomp; my ($key, @values) = split /\s+/; $data{$key} = [ @values ]; } print $data{key2}->[1], "\n"; # prints: e __DATA__ key1 a b c key2 d e f key3 g h i

    See the Perl data structure's cookbook and perllol for information about Perl's data structures.

      I'm using the stucts from Class::Struct, can I get away with just defining one struct and then reusing that struct over and over for each line? i.e. I guess my real question is, when you push something onto a Hash, does it push the value, or just a pointer? Hence, if I reuse the same struct, and the Hash just had pointers, I'd be overwriting the previous variables... Thanks.

        does it push the value, or just a pointer?

        When you're passing around objects, really you're passing around special references. So even if you have two scalars pointing to one struct, it's that same struct you're modifying, regardless of how it's named.

        It might be easier to illustrate with an example:

        use Class::Struct; use strict; struct Test => [ xyz => '$' ]; our $T = new Test(xyz => 123); print "\$T->xyz starts as: ", $T->xyz, "\n"; no strict 'refs'; my $symbolic = "T"; ${$symbolic}->xyz(456); print "\$T->xyz is now: ", $T->xyz, "\n"; my %A $A{t} = $T; $A{t}->xyz(789); print "\$T->xyz is now: ", $T->xyz, "\n"; __END__ $T->xyz starts as: 123 $T->xyz is now: 456 $T->xyz is now: 789
        To get "full" copy effects, you need to make a copy of each of the fields:
        my $a = new Test ( xyz => 123 ); # original my $b = new Test ( xyz => $a->xyz ); # copy of a $b->xyz(456); printf "a=%d b=%d\n", $a->xyz, $b->xyz; # a=123 b=456

        I don't know of an easy way to "deep copy" struct objects, though the Clone module (or Storable's 'dclone' function) may help.

        You don't really push to a hash, you can - but push is usually reserved for array (and yes, a hash is an array). My point is that you assign a value to a hash key - that value can be a scalar (the value), a reference (the pointer), or other things.

        Here is an example that reads 'configuration info' from Perl's built in DATA filehandle. I use DATA because it's easy and you only need one file for demonstrations. Anyhow, the configuration is simply two values seperated by a comma per line. The while loop processes DATA one line at a time and creates a new struct object with the values it finds. After the struct is instantiated, it is pushed to an array. Since it is already a reference there is no need to de-reference it. The last line is a for loop that prints out only one of the attributes for each struct in the array.

        use strict; use Class::Struct; # declare the struct struct(MyStruct => { even => '$', odd => '$', }); # create array of structs my @structs; while (my $line = <DATA>) { chomp $line; my ($odd,$even) = split(',',$line); my $obj = new MyStruct; $obj->odd($odd); $obj->even($even); push(@structs,$obj); } print $_->odd(), "\n" foreach (@structs); __DATA__ one,two three,four five,six seven,eight nine,ten
        But hold it right there! Why are you using Class:Struct? No offense to the author, but if you are going to code in Perl, code like Perl. Check this out:
        use strict; # create array of structs my @structs; while (my $line = <DATA>) { chomp $line; my ($odd,$even) = split(',',$line); my %hash = ( odd => $odd, even => $even, ); # i do have to de-reference %hash though push(@structs,\%hash); } print $_->{'odd'}, "\n" foreach (@structs); __DATA__ one,two three,four five,six seven,eight nine,ten
        Same thing with less lines, and i spared using map to golf it down even further ... oh what the hell:
        print $_->{odd},"\n"for map{my($o,$e)=split',',$_;{odd=>$o,even=>$e}}< +DATA>;
        Welcome to Perl. :)

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        F--F--F--F--F--F--F--F--
        (the triplet paradiddle)
        
Re: Dynamically allocating variables
by wog (Curate) on Dec 02, 2001 at 05:28 UTC
    Instead of trying to use variable as variable names (as you are suggesting), you should use data structures. See also perldsc. (update: Of course, Fastolfe beat me to referencing this document.)

    There is a reason that perl does not let you use variables as variable names under strict; it is not an arbitrary requirement. See this set of articles for reasons why and examples of code transformed from using a variable as a variable name to not doing so.