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

I was using a var varname, but googling turned up pitfalls of that. So I am wondering is there a way to implement what I am doing using hashes? Heres the code
@diffarray = (test, info, path.....) ## Its machine generated so can +have 15-20 elements too foreach $element (@diffarray) { @{$element}_diffresult = {$element}_subroutine(); } ## The subroutines are also machine generated
Now I can use a hash like my %results for diffresult and %dispatcharray for my subroutine dispatching. But I do not know what all elements can be in @diffarray. So I am using var varname. Its giving me "Bareword error"

Replies are listed 'Best First'.
Re: Alternative to varvarname
by Corion (Patriarch) on Apr 07, 2006 at 11:40 UTC

    "The subroutines are also machine generated" hints to me that all those subroutines are similar. So without any deeper reasons as to why not, I would propose removing the machine generated code and replacing it with a few, parametrized subroutines. If you don't want to do that, here's how you can easily hang yourself by creating horrible code that is short of goto EXPR:

    use strict; use Data::Dumper; ... my %results; foreach my $name (@diffarray) { my $code_name = "${name}_diff"; no strict 'refs'; my $code = \&{ $code_name }; $results{ $name } = [ $code->() ]; }; print Dumper \%results;

    Your error comes from the fact that you are not properly interpolating the names of the subroutines.

    Of course, you should really avoid the construction of symbolic references, for all the reasons that varvarname lists. In your case, this is done by using (for example) a second hash that lists all the subroutines and the names under which they may be accessed:

    use strict; use Data::Dumper; ... my %results; my %dispatch = ( test => \&test_diff, info => \&info_diff, path => \&path_diff, ... ); foreach my $name (@diffarray) { if ( ! exists $dispatch{$name}) { die "Don't know how to handle name '$name'."; }; my $code = $dispatch{$name}; $results{ $name } = [ $code->() ]; }; print Dumper \%results;

    Doing it this way, the code becomes cleaner and you get more convenient error handling.

Re: Alternative to varvarname
by GrandFather (Saint) on Apr 07, 2006 at 11:38 UTC

    If you can be bothered with an OO approach you can do this:

    use strict; use warnings; my @diffarray = qw(test info path); sub new { return bless {}; } sub run { my $self = shift; foreach my $element (@diffarray) { my $member = "do_$element"; if ($self->can($member)) { $self->{$element} = $self->$member (); } else { print "Unhandled mode $element\n"; } } } sub do_test { return 'test'; } sub do_info { return 'info'; } sub do_path { return 'path'; } my $test = new (); $test->run (); print join "\n", map {"$_: " . $test->{$_}} sort keys %$test;

    Prints:

    info: info path: path test: test

    To add a new handler just add the appropriately named sub.


    DWIM is Perl's answer to Gödel
Re: Alternative to varvarname
by aquarium (Curate) on Apr 07, 2006 at 11:49 UTC
    try closures for the subs and a hash of arrays...as using user/computer generated values for varnames are generally frowned upon and produce horrible code. hashes of arrays and closures, on the other hand, are easily coded for in functional/top-down/modular way.
    the hardest line to type correctly is: stty erase ^H