Peter Lyko
Peter Lyko

Reputation: 61

Can I reference a named subroutine with some arguments

I have a subroutine taking multiple arguments and want to make a reference to it with one of the arguments set, so that the reference takes one argument less. Optimal case would be

my $subref = \&routine($arg1);
...
my $result = $subref->($arg2,$arg3);

In perlref there is an example with an anonymous subroutine like this, however I cannot get the equivalent with a named one working.

Below is a full fledged example of what I mean. While $func (ref to anonymous sub) and $func2 (ref to named sub, but without arguments) work. $func3 gives the error "Not a CODE reference[...]".

Have I missed something or is this actually impossible?

use strict;
use warnings;

sub args{
    my $arg1 = (shift or "none");
    my $arg2 = (shift or "none");
    my $arg3 = (shift or "none");
    my (undef, undef, undef, $function) = caller(0);
    return "me: $function\narg1 = $arg1\narg2 = $arg2\narg3 = $arg3\n";
}

sub just_a_ref {
    return \&args;
}

sub new_arg_anon {
    my $arg = shift;
    return sub{
        my $arg1 = $arg;
        my $arg2 = (shift or "none");
        my $arg3 = (shift or "none");
        my (undef, undef, undef, $function) = caller(0);
        return "me: $function\narg1 = $arg1\narg2 = $arg2\narg3 = $arg3\n";
    }
}

sub new_arg {
    my $arg = shift;
    return \&args($arg);
}


my $func = new_arg_anon("one");
print $func->("two","three");     #works fine
my $func2 = just_a_ref();
print $func2->("un", "deux", "trois");  #works fine
my $func3 = new_arg("eins");
print $func3->("zwei", "drei");   #Not a CODE reference


Upvotes: 3

Views: 151

Answers (2)

mob
mob

Reputation: 118625

\&args($arg) is \( args($arg) ), that is, a reference to the return value of the function call args($arg), not a reference to the function args called with the argument $arg.

print $func3;   # SCALAR(0x8000a1a50)

To make a reference to a function that executes the args subroutine with $arg as the first argument, use

sub new_arg {
    my $arg = shift;
    return sub { args($arg,@_) };
}

(look at that, just like Georg Mavridis's answer)

Upvotes: 3

Georg Mavridis
Georg Mavridis

Reputation: 2341

You have to create a new anonymous function that does exactly that. Call the target function with one argument set and passing the rest of the arguments to it. In your example the new_arg function should be:

sub new_arg {
    my $arg = shift;
    return sub {args($arg, @_)};
}

Upvotes: 4

Related Questions