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

I'm trying to use push with a variable being substituted, or maybe I'm phrasing it wrong, Take a look:
#(no this isn't my actuall code but a simplified example) @vars =("num", "alpha" ); $num=1; $alpha="a"; for ($i=0; $i<@vars;$i++){ # create an array @num with value from $num, etc # create an array @alpha with value from $alpha, etc push ( @($vars[$i]), $($vars[$i]); }
So, I'm trying to make push really see it as:
push (@num, $num) and push (@alpha, $alpha)
except I've got to do this with about 20 arrays, therefore the need for "variable substition" to keep the code clean. How do I do this ?

Replies are listed 'Best First'.
RE: push with variable substitution ?
by gnat (Beadle) on Jun 19, 2000 at 22:33 UTC
    Here's a more detailed response than you've received yet.

    Any time you find yourself saying "I have the name of a variable in a string, and I want the value in that variable", stop.

    You're programming like a Perl 4 programmer. You are using the symbol table to build your data structures. Back in those days you couldn't have an array of hashes. But you could have an array containing the names of hashes. Here you're building a data structure that's a hash whose values are arrays. You're using the symbol table as the top-level hash.

    The solution is to use Perl 5 programming techniques, namely references. You can actually have a hash of arrays, so when you look up a string like "alpha" or "num" you get back a reference to an array.

    Your code would then look like:

    foreach $var (@vars) {
      push @{ $myhash{$var} }, $var;
    }
    
    It looks like you're reading a file a record at a time. Each record contains a bunch of fields. You want an array for each field, so that $name[0] is the name of the 0th person, $age[0] is the age of the 0th person, etc.

    Each record you read, you split into $name, $age, and so on. Then you need some magic to push $name onto @name, $age onto @age, etc.

    There is two solutions to this problem. The first is to split into a hash: $person{name}, $person{age}, etc. Then you can do:

    foreach $field (keys %person) {
      push @{ $myhash{$field} }, $person{$field};
    }
    
    A better approach, depending on what you're going to do with the data, might be to have an array of records. Then for each record, you simply do:

      my %record;
      # populate $record{NAME}, $record{AGE}, etc
      push @records, \%record;
    
    Now if you want to talk about the 5th person's name, say:
    $records[5]{NAME}
    Definitely learn about references, though. The latest versions of Perl come with MjD's excellent perlreftut manpage, which is a great place to start.
Re: push with variable substitution ?
by merlyn (Sage) on Jun 19, 2000 at 22:10 UTC
Re: push with variable substitution ?
by eduardo (Curate) on Jun 19, 2000 at 22:18 UTC
    i agree with merlyn 100%, what you are trying to do is basically (as far as I understand it) symbolic references. And let me tell you, that is the slippery slope to hell. Very few projects that use symbolic references will be able to either scale well or be able to be undertood a few months after you have written it. On top of it all, as far as I understand it, it wont work with strict (back me up on this one, or correct me...) Analyze your problem a bit more and attempt to make the logic fit the data, not the data fit the logic, I have never encountered a program where i NEEDED symbolic references, where I couldn't either use hashes or rethink my problem a bit more. As merlyn said what you want to do is something along these lines:
    my $h = { "num" => [1], "alpha" => ["a"]}; my @vars = ("num", "alpha"); foreach my $val (@vars) { push(@{$h->{$val}}, $val); }
    i think.... then again, i may not understand your problem...
Re: push with variable substitution ?
by lhoward (Vicar) on Jun 19, 2000 at 22:13 UTC
    This can be done with symbolic references. Be careful as symbolic references can be dangerous. This code will not run with "use strict".
    @vars =("num", "alpha" ); $num=1; $alpha="a"; foreach(@vars){ @$_=$$_; }
    Now that I've shown you how... don't ever use symbolic references. As merlyn pointed out above, there are better ways to have a functionally equivalent end result that aren't nearly as nasty or error-prone.
    my %vars=(num => 1, alpha => 'a'); my %h; foreach(keys %vars){ $h{$_}=[$vars{$_}]; }
    The syntax for accessing the arays is slightly different (because of the extra level of indirection in there), but this is a much cleaner and less error-prone approach.