Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Hash of Hash of Listed subroutines

by c (Hermit)
on Jan 10, 2003 at 00:34 UTC ( #225696=perlquestion: print w/replies, xml ) Need Help??

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

I'm very new to HoHoL or HoH for that matter. Basically I have a hash of hashes. One of the values of a nested hash is an array of subroutine references that I would like to execute.

#!/usr/bin/perl -w use strict; my %HoL = ( one => [ (\&hello, \&goodbye,) ], two => [ (\&hello,) ], ); for my $key(%HoL) { for my $i(@{$HoL{$key}}) { print "$key -> &{$HoL{$key}->{$i}}\n"; } } sub hello { print "hello"; } sub goodbye { print "bye"; }

I've messed around with replacing the sub routine references with just text values and looping over them for printing and things go alright. So, I'm suspecting that there is a problem in the manner that I am calling the subroutine. I've messed around with different manners of calling the routines as well as just calling the first with &{$HoL{$key}[0]}

for my $key(%HoL) { &{$HoL{$key}[0]}; }

which seems to be successful but still produces an error complaining about a "" string being used as a subroutine. I'm just not sure where that is coming from since both keys have arrays with at least one element.

I appreciate your advice. Thanks -c

Replies are listed 'Best First'.
Re: Hash of Hash of Listed subroutines
by ihb (Deacon) on Jan 10, 2003 at 01:27 UTC
    Since answers to your question have been given already I just felt like writing the test code without a lot of extra variables, just because I love map: $_->() for map @$_, values %HoL; If you don't want all values, and/or want the subroutines in order, you can do: $_->() for map @$_, @HoL{qw/ one two /}; I don't know if it's more readable, but personally I find it more concise and more straight to the point than nestled for loop with extra and only once used variables.

    If you want the return values from the callbacks it gets extra nifty: @ret = map $_->(), map @$_, @HoL{qw/ one two /}; I also want to warn about dereferencing the callback without parentheses, as often seen when using the & to dereference it. It doesn't do what you think, and the issue is covered in perlfaq7, "What's the difference between calling a function as &foo and foo()?".

    Looking at the code again though, I noticed that you do (\&hello, \&goodbye,) There's nothing wrong with that, but I figured you'd perhaps be glad to know that you can use the dangerously-looking \(&hello, &goodbye,) instead to save yourself a couple of backslashes. No, the subroutines does not get called. \(...) is quite special. Be sure to check it out in perlref (perlop will just point you there anyway).

    Cheers,
    ihb

    PS. Yes, I love map. :)
Re: Hash of Hash of Listed subroutines
by djantzen (Priest) on Jan 10, 2003 at 00:44 UTC

    The way to dereference a subroutine reference is to use either & slightly differently than you have, or better, to use parentheses so that you can pass arguments to the subroutine. For clarity, I'll switch your outer loop to a while with an each.

    while (my ($key, $subs) = each %HoL) { foreach my $sub (@$subs) { $sub->(); # or &$sub; } }

    To go back to your example, one problem is that you're looping over the whole hash, where normally you'd want to use keys or values. This works:

    for my $key (keys %HoL) { for my $i (@{$HoL{$key}}) { $sub->(); # or &$i; } }

Re: Hash of Hash of Listed subroutines
by Kanji (Parson) on Jan 10, 2003 at 00:39 UTC

    Something that stands out right off the bat is you want to loop over the keys not the entire hash, like so...

    for my $key(keys %HoL) { &{$HoL{$key}[0]}; }

    Also, you cannot embed subs inside strings without resorting to trickery, so instead you can one of the following ...

    print "$key -> ", &{$HoL{$key}->{$i}}, "\n"; print "$key -> @{[ &{$HoL{$key}->{$i}} ]}\n"; # voodoo!

        --k.


Re: Hash of Hash of Listed subroutines
by jdporter (Canon) on Jan 10, 2003 at 00:49 UTC
    ...I'm suspecting that there is a problem in the manner that I am calling the subroutine.
    Yep. If you rename that $i variable to $subref, it will become apparent what you can do next.
    The other thing to remember is that you can't embed a function call directly in a string. You can assign the result to a variable (scalar or array) and embed that; or you can pass multiple arguments to print(), as I do below.
    for my $key ( %HoL ) { for my $subref ( @{ $HoL{$key} } ) { print "$key -> ", &{$subref}, "\n"; } }
    (P.S. -- whitespace good. :-)

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

Re: Hash of Hash of Listed subroutines
by OM_Zen (Scribe) on Jan 10, 2003 at 18:53 UTC
    Hi ,

    The Structure of hash cannot be used as the structure in for ,only arrays can be used for for and foreach and hence you have to have

    for my $Key(keys %Hashofsubshashes){ for my $i(@ {$Hashofsubshashes{$Key}}){ print "$Key->&{$Hashofsubshashes{$Key}->{$i}}\n"; } }


Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://225696]
Approved by Kanji
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (1)
As of 2021-06-19 09:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What does the "s" stand for in "perls"? (Whence perls)












    Results (91 votes). Check out past polls.

    Notices?