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

I'm working on a module, and I'm trying to do something like this. Only it doesn't work:
package Foo; sub new { my($pkg, %args) = @_; my $self = { method => 'correlation', %args }; $self->{funcs}{correlation} = \&$self->correlation; return bless $self, $pkg; } sub correlation { print "This is the correlation function!"; }
The idea here is that I'll have a variety of drop-in functions, and it'd be nice to be able to do something like:
my $x = new Foo(method => 'biscuit');
and just have a check in the Foo module:
if exists $self->{funcs}{$self->{method}} { $self->{funcs}{$self->{method}}(params); } else { die "Sorry, the method $self->{method} isn't valid!"; }
However, trying this in various ways gives me various error messages:
#This says "Not a CODE referece at Foo.pm line..." $self->{funcs}{correlation} = \&$self->correlation; #This says "Can't call method 'correlation' on unblessed # reference at Foo.pm line..." $self->{funcs}{correlation} = \&{$self->correlation}; #And this says "Scalar found where operator expected at # Foo.pm line... $self->{funcs}{correlation} = \&($self->correlation);
So, any suggestions? I dug around for quite a while but failed to find any information on this. Help would be most appreciated (as would suggestions on how to do this other ways)!

Thanks!

dan

Replies are listed 'Best First'.
Re: reference to self function
by chromatic (Archbishop) on Jun 25, 2003 at 06:59 UTC
    if ($self->can($self->{method})) { my $method = $self->{method}; $self->$method(@params); } else { die "Sorry, the method $self->{method} isn't valid!"; }

    You might want to restrict access by prefixing a name or a symbol to all methods you want to call this way.

    One other tip: the indirect object notation is perilous. I prefer to call constructors with:

    my $x = Foo->new(method => 'biscuit');

    It's a lot clearer that the method is new, not Foo.

      I didn't know about "can" - that's pretty neat. But I'd really like to make a reference I can store, since I'll be calling the "method" function throughout the program; what I have in the meantime looks like this:
      sub w { my($self, $a, $i) = @_; if($self->{method} eq 'correlation') { return $self->correlation($a, $i); } if($self->{method} eq 'vector_similarity') { return $self->vector_similarity($a, $i); } if($self->{method} eq 'IUF') { return $self->IUF($a, $i); } if($self->{method} eq 'MSD') { return $self->MSD($a, $i); } }
      The w function gets called quite a few times, so I don't want a per-call check.

      Thanks for the tip on constructors; that's a good point.

      dan

Re: reference to self function
by pernod (Chaplain) on Jun 25, 2003 at 07:41 UTC

    Why are you using symbolic references?I have learnt a lot on manipulating references in objects from tilly's1 and Schemer's2 thoughtful nodes. tillys's node talks about functional programming techniques in Perl. It seems to me that it would be a good idea to actually store the references to functions in the hash, not just their names. They're the ones you're working with, after all, so why force perl to take the extra step?

    Anyhow, based on these nodes, I came up with the following:

    package DynamicObject; sub new { my ( $pkg, %args ) = @_; my $self = { "method" => \&correlation, %args }; return bless $self, $pkg; } sub correlation { print "This is the correlation function!\n"; } sub setMethod { my ( $self, $key, $function ) = @_; $self -> { $key } = $function; } sub callMethod { my ( $self, $function, @arguments ) = @_; if( exists $self -> { $function } ) { &{ $self -> { $function } }( @arguments ); } else { print "Unknown function '$function' called.\n"; } } 1;

    It is used like so:

    use DynamicObject; my $object = DynamicObject -> new (); $object -> callMethod( "method" ); $object -> setMethod( "method", \&another ); $object -> callMethod( "method" ); $object -> callMethod( "fnap" ); $object -> setMethod( "fnap", \&ya ); $object -> callMethod( "fnap", qw{ Hey ho! We wont go!} ); sub another { print "This is another function!\n"; } sub ya { print "This is yet another function called with ". "arguments '", join( " ", @_ ), "'!\n"; }

    Which outputs:

    This is the correlation function! This is another function! Unknown function 'fnap' called. This is yet another function called with arguments 'Hey ho! We wont go +!'!

    callMethod() has a simple error-check to see whether the object actually has the method. Any arguments passed to callMethod() is forwarded to the the method in question, if it exists.

    I really, really recommend the two above mentioned nodes to anyone playing around with references in Perl. Hope this helps.

    Update: After a reread, I realize that my tone in this post was a bit arrogant. I have rewritten parts of the message to reflect my respect of both the problem at hand and the nodes i refer to. I offer my apologies to those I have offended.

    pernod
    --
    Mischief. Mayhem. Soap.
Re: reference to self function
by ihb (Deacon) on Jun 25, 2003 at 12:49 UTC
    You've already been tipped about can, but it seems like you missed that it returns a reference, which is exactly what you want to store.
    my $m = $self->{method}; $self->{funcs}{$m} = $self->can($m);
    Then you use
    if (my $code = $self->{funcs}{$self->{method}}) { $self->$code(@params); } else { die "..."; }
    Why don't you do this check in the constructur, btw?

    Note that this isn't equivalent to   $self->{funcs}{$m} = sub { $self->$m(@_) }; You'd get in trouble with the closure solution if you ever decide to clone your object. Then you'd still be using your old object.

    Hope this helps,
    ihb
Re: reference to self function
by sgifford (Prior) on Jun 25, 2003 at 06:43 UTC
    What about:
    $self->{funcs}{correlation} = \&Foo::correlation;
    ? Seems to work here.
      Unfortunately in the real code, it looks like a call doesn't include $self as an argument. Apologies for not making that need clear earlier. For example, making correlation look like:
      sub correlation { my($self, @args) = @_; print "self is $self, there are @args args\n"; }
      and creating a new function bar that looks like:
      sub bar { my $self = shift; $self->{funcs}{$self->{method}}(); }
      When we do:
      my $x = new Foo; $x->bar;
      the output is errors about uninitialized values, and self is , there are  args

      dan

        Ah, then perhaps you want:
        $self->{funcs}{correlation} = sub { $self->correlation(@_) };
        ?