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

I have a multi-dimentional data structure, a mix of hashes and arrays. An example would be $$hash{domains}{sub1}{sub2}. If I want to get something out of it I know all I need to do is $hash->{$domain}{$var1}{$var2}. However, I want to pass {$var1}{$var2} to a subroutine so I can:

foreach my $domain (sort {$a cmp $b} keys %{$hash}) { my $value = $hash->{$domain}$test; }

where the variable $test is actually the code {$var1}{$var2}. I've tried my $test = "{var1}{$var2}" but I get an unblessed reference error.

How do I do I get perl to look at $test not as a the string "{$var1}{$var2}" but as the perl code {$var1}{$var2} ? Can I do this at all?

Replies are listed 'Best First'.
Re: unblessed reference problem
by Corion (Patriarch) on Mar 26, 2010 at 11:08 UTC

    Have a look at Data::Diver, if you want to dive dynamically down a datastructure. Alternatively, you can look at eval, but using eval will open up security holes and you will have to make sure all your tests are well-formed Perl syntax and semantically valid (that is, $var1 and $var2 need to be defined.

      I've tried using eval without success:

      my $answer = $hash->eval($test)

      I can "can't call method eval". I've never used eval before any hints?

        I linked to the documentation of eval for a reason. Nowhere in the documentation, your form of use is mentioned. That doesn't mean that your version does not work, but I wonder how you deduce that expectation from the documentation you read.

        *sigh* even more reason you shouldn't go near eval
Re: unblessed reference problem
by james2vegas (Chaplain) on Mar 26, 2010 at 11:36 UTC
    Step through each key you pass in, and recurse into each hash with that key, like this (but not so ad-hoc and ugly):

    EDIT: Updated to deal with hashes and arrays.

      Thanks james2vegas, that is definitely a very good solution to this specific problem. What I really want to know is how to get perl to do what I originally posted. I'm well aware some of the more esoteric solutions might be dangerous, but I still want to know how it works to further my knowledge.

        but I still want to know how it works

        If you're referring to the eval thing, that would be:

        #!/usr/bin/perl -l use strict; use warnings; my $hash = { domain => { foo => { bar => "baz" } } }; my $domain = "domain"; my $var1 = "foo"; my $var2 = "bar"; my $test = '{$var1}{$var2}'; my $value = eval '$hash->{$domain}'.$test; print $value; # "baz"

        That said, better stay away from eval unless you know what you're doing, because

        • it's potentially dangerous when you overlook something (e.g. when the value of $test might originate from somewhere not under your full control)
        • code using it is unnecessarily hard to read and maintain
        • it's unnecessarily slow
        *sigh* To make $hash->{$domain}$test; into $hash->{$domain}{$var1}{$var2} you have to write $foo such that print $foo prints $hash->{$domain}{$var1}{$var2}, then you can eval $foo to execute.
Re: unblessed reference problem
by roboticus (Chancellor) on Mar 26, 2010 at 12:03 UTC

    gdolph:

    You may want to rearrange your hash so that the domain is the *last* rather than the first key. That way, you can do:

    foreach my $domain (sort {$a cmp $b} keys %{$$hash{$var1}{$var2}}) + { my $value = $$hash{$var1}{$var2}{$domain} }

    ...roboticus

      I forgot to mention that you can use a continuation closure, something like this (untested):

      my $test = sub { my $hr=shift; return $hr->{$var1}{$var2} }; foreach my $domain (sort {$a cmp $b} keys %{$hash}) { my $value = &{$test}($$hash{$domain}); }

      ...roboticus

      Update: correction, as noted by chromatic. (I didn't make the other change, as it's merely ugly rather than wrong, and chromatic shows the nicer looking version anyway.)

        Two small nits. First, that isn't a continuation. It's a closure. (It closes over variables, not control flow.)

        Second, your dereferencing syntax could be much clearer:

        my $value = $test->($hash->{$domain});

      Would that I could! If could just then hand a reference to that particular branch, unfortunately to do so would require a major re-working of everything further up so wouldn't be worth the effort