tags:

views:

138

answers:

5

Let's say I have the following piece of code:

my $compiled = eval 'sub { print( "Hello World\n" ); }';

I can call this by writing:

$compiled->();

So far so good. Now imagine I create 10 functions:

my @fns = ();
for ( my $i = 0; $i < 10; $i++ ) {
  push( @fns, eval "sub { print( 'I am function $i\n' ); }" );
}

I can call these 10 functions as follows:

foreach ( @fns ) {
  $_->();
}

Now, I want to create a dynamic function that calls each of my 10 functions explicitly:

my $evalcode = "sub {";
foreach ( @fns ) {
    # if I print $_ it shows something like
    #   "CODE(0x94084f8)", but trying to
    #   call "CODE(0x94084f8)->()" is invalid
    $evalcode .= "$_->();";
}
$evalcode .= "}";


my $dynamic_fn = eval $evalcode;
$dynamic_fn->();

Is it possible to take a stringified reference to a subroutine and call this directly?

PS why, you ask? Because I would like to write a dynamic routine that constructs a chain of if ( m/.../ ) { } elsif ( m/.../ ) { } ... checks that then call dynamic functions depending on the input string.

+13  A: 

Instead of string evals, you might want to use regular lexical closures:

my @functions;

for my $i (0 .. 9) {
    push @functions, sub { print "I am function $i\n" };
}

my $call_all_funcs = sub {
    for my $func (@functions) {
        $func->();
    }
};

$call_all_funcs->();

It is also possible to retrieve a code reference based in its address, but that'd be much more elaborate, harder to understand, and generally not a very good idea.

rafl
I was going to suggest this, but I don't think it will do for the OP. I think the code of the different subs is to be generated dynamically, which is why they want to use eval.
Colin Fine
It will. The second closure is the important bit. The list of functions generated earlier could also be generated with a string eval. But then again I still think there's ways to do whatever the OP wants without having to resort to `eval STRING` at all. Closures are really quite powerful.
rafl
+7  A: 

How about using a closure instead of an eval string?

sub combine_subrefs {
  my @subs = @_;
  return sub { foreach my $subref (@subs) { $subref->() } };
}

my $dynamic_fn = combine_subrefs( @fns );
$dynamic_fn->();

I'm sure you can adapt this to do the elsif thing you mentioned as well.

aschepler
+1  A: 

It might be possible (see this similar problem and solutions), but maybe there is another way. How about a (global?) hash that maps stringified code references to the actual code references?

my @fns = ();
for ( my $i = 0; $i < 10; $i++ ) {
  my $fn = eval "sub { print('I am function $i\n'); } ";
  if (ref $fn eq 'CODE') {
      $CODETABLE{$fn} = $fn;
  }
  push @fns, $fn;
}

...

my $evalcode = "sub {";
foreach ( @fns ) {
    # convert stringified code ref to the actual code ref
    $evalcode .= "\$CODETABLE{\"$_\"}->();";
}
$evalcode .= "}";

(eval $evalcode)->();

I am function 0
I am function 1
I am function 2
I am function 3
I am function 4
I am function 5
I am function 6
I am function 7
I am function 8
I am function 9
mobrule
If lexical closures work, though, then that is the way to go.
mobrule
+6  A: 

re: why, you ask? Because I would like to write a dynamic routine that constructs a chain of if ( m/.../ ) { } elsif ( m/.../ ) { } ... checks that then call dynamic functions depending on the input string.

To create an if & elsif chain like you described above can be done without having to resort to eval:

use 5.012;
use warnings;

my $build_ifelse_dispatch_table = sub {
    my @functions = @_;

    sub {
        my $text = shift;
        for (@functions) {
            my ($re, $code) = @$_;
            if ($text =~ $re) {
                $code->();
                last;
            }
        }
    };
};

my $dispatch_on = $build_ifelse_dispatch_table->( 
    [ qr/tom/   => sub { say "Tom!"   } ],   # if    (m/tom/)   { ... }
    [ qr/dick/  => sub { say "Dick!"  } ],   # elsif (m/dick/)  { ... }
    [ qr/harry/ => sub { say "Harry!" } ],   # elsif (m/harry/) { ... }
);


$dispatch_on->( 'peeping tom'         );  # Tom!
$dispatch_on->( 'spotty dick pudding' );  # Dick!
$dispatch_on->( 'harry potter'        );  # Harry!
$dispatch_on->( 'Tom, dick and harry' );  # Dick!

ref: Wikipedia entry on Dispatch Table.

/I3az/

draegtun
+3  A: 

You're seeing something like CODE(0xDEADBEEF) when you print a subroutine reference because that's how Perl stringifies references. You see the say sort of thing if you print any sort of reference that doesn't overload stringification:

 $ perl -le 'print []'
 ARRAY(0x1008032b8)

Normally you can't really use that value for anything, and the number you see doesn't correspond necessarily to a real memory address.

For what you're doing, see the dynamic subroutine chapters I have in Mastering Perl. I talk quite a bit about the different things you can do to compose subroutines and work with anonymous subroutines. A module such as Data::Constraint might even give you some ideas. I also talk about this in my answer to How can a Perl force its caller to return?.

brian d foy