http://qs1969.pair.com?node_id=280921

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

Hi All,

I have written a multipage CGI script using the technique described in recipe 19.12 of the Perl Cook book. Namely put all the HTML generation/program logic in subroutines and store those subs as values of a hash.

This all works fine until you need to pass an argument when declairing the code ref within the hash. When this is done the subroutine is executed (I assume this has to be done because the sub has to be exaluated because of the arg).

This totally breaks my multipage CGI script as one page is only ever displayed. How do I fix this?

Here is an example that demonstrates the problem:

sub print_me { my $arg = shift || "Hello World!\n"; print $arg; } %hash = {no_arg => \&print_me, with_arg => \&print_me("Hello World, again!\n" }; $hash{no_arg}->();

__OUTPUT__

Hello World, again!\n";

I'm using Perl 5.6.1 on Linux

!unlike

I write my Perl code like how I like my sex: fast and dirty. ;)

Replies are listed 'Best First'.
Re: Declaring a code ref with arguments
by Abigail-II (Bishop) on Aug 05, 2003 at 09:12 UTC
    %hash = (no_arg => \&print_me, with_arg => sub {print_me "Hello World, again\n"}, );

    Abigail

      D'oh! Of course! Silly me.

      Thanks to all who repied. The fix worked a treat.

      Cheers

      !unlike

      I write my Perl code like how I like my sex: fast and dirty. ;)

Re: Declaring a code ref with arguments
by broquaint (Abbot) on Aug 05, 2003 at 09:13 UTC
    The best way to do this generally is to pass in an anonymous sub which executes the sub with the desired arguments e.g
    sub print_me { my $arg = shift || "Hello World!\n"; print $arg; } %hash = ( no_arg => \&print_me, with_arg => sub { print_me("Hello World, again!\n") }, ); $hash{no_arg}->(); $hash{with_arg}->(); __output__ Hello World! Hello World, again!
    Also, you want parens, not curly braces when assigning to a hash as curly braces will return a single element of an anonymous hash reference, whereas using parens will return a list (in this case) which will duely populate %hash. See. perlreftut and tye's References quick reference for more info.
    HTH

    _________
    broquaint

Re: Declaring a code ref with arguments
by BrowserUk (Patriarch) on Aug 05, 2003 at 09:13 UTC

    The simple answer is that you cannot store a coderef with arguments. What would that mean if you could?

    sub test{ print @_; } ## THIS DOESN'T WORK, BUT IF IT DID... my $coderef = \&test( 'this is the argument' ); $coderef->(); # would print 'this is the argument' ); $coderef->( 'a different argument' ); # Would print 'this is the argument'!!

    If you really want the sub to always have the same argument(s), then they aren't argument(s)--they are constant(s) :) -- so code the sub that way.

    sub test{ print 'this is the argument'; } my $coderef = \&test; $coderef->(); # prints 'this is the argument' ); $coderef->( 'a different argument' ); # Prints 'this is the argument'!!

    Problem solved:)

    However, if you want to pass different arguments when you invoke the coderef, do so.

    my $coderef = sub{ print @_; }; $coderef->( 'Some', 'args' ); # Prints 'someargs'

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
    If I understand your problem, I can solve it! Of course, the same can be said for you.

Re: Declaring a code ref with arguments
by Juerd (Abbot) on Aug 05, 2003 at 10:13 UTC

    In Perl 6, you will be able to use assuming:

    sub print_me ($arg = "Hello World!\n") { print $arg; } %hash = ( no_arg => &print_me, with_arg => &print_me.assuming(arg => "Hello World, again!\n") ); %hash{no_arg}();

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Re: Declaring a code ref with arguments
by cianoz (Friar) on Aug 05, 2003 at 09:16 UTC
    have you ever tested your code? this is broken!!
    however you should look at perldoc perlref on how to build code refs..
    this is a working version:
    #!/usr/bin/perl -w use strict; sub print_me { my $arg = shift || "Hello World!\n"; print $arg; } my %hash = (no_arg => sub { &print_me()}, with_arg => sub {&print_me("Hello again!\n")} ); $hash{no_arg}->(); $hash{with_arg}->();

      The example I provided did not use strict. My apologies for that oversight. With that being the case the code worked fine (even with -w)

      Cheers

      !unlike

      I write my Perl code like how I like my sex: fast and dirty. ;)

      His argument passing is wrong but it sounds like you are saying that this:

      my %hash = ( foo =>\&do_foo, bar =>\&do_bar, );

      is wrong, (correct me if I'm misinterpreting your post) which I'm not sure where you find that in perlref as that works, even while using strict. To pass the arguments you can do this:

      $hash{foo}->('foo','bar','baz');
      Lobster Aliens Are attacking the world!
        no, at all, if you look carefully at what he posted you'll find this:
        %hash = {no_arg => \&print_me, with_arg => \&print_me("Hello World, again!\n" };
        as you can see he uses curly brackets to build a hash and lacks right parenthesis just after "Hello World, again!\n"
        what i say is that this code will never work (maybe is just a wrong trascription) and that you can't do
        bar =>\&do_bar("argument")
        so you have to do
        bar => sub { do_bar("argument") }
        that's all..
        update of course (foo => \&foo) with no arguments is perfectly legal..