2010-10-01 68 views
4

比方说,我有如下一段代码:我可以在Perl的硬编码地址中调用子程序吗?

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

我可以写调用这个:

$compiled->(); 

到目前为止好。现在想象我创造10个功能:

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

我可以打电话给这10个功能如下:

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

现在,我想创建一个调用我的每一个10个功能明确动态功能:

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->(); 

是否可以对子例程进行字符串化引用并直接调用它?

PS为什么,你问?因为我想编写一个动态例程,构造一个if (m/.../) { } elsif (m/.../) { } ...检查链,然后根据输入字符串调用动态函数。

回答

14

相反串evals的,你可能需要使用常规的词汇闭包:

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->(); 

也可以检索基于在其地址代码的参考,但会是更复杂的,难以理解,通常不是一个好主意。

+0

我打算提出这个建议,但我认为它不会为OP做。我认为不同潜艇的代码是动态生成的,这就是为什么他们想要使用eval。 – 2010-10-01 15:07:05

+7

它会的。第二次关闭是重要的一点。之前生成的函数列表也可以使用字符串eval生成。但是,我仍然认为有办法做OP的任何事情,而不必依赖于'eval STRING'。关闭非常强大。 – rafl 2010-10-01 15:09:18

8

如何使用闭包而不是eval字符串?

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

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

我相信你可以适应这个做elsif事情你也提到。

1

这可能是可能的(见this similar problem and solutions),但也许有另一种方式。将字符串化代码引用映射到实际代码引用的(全局?)散列怎么样?

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 
+1

但是,如果词法封闭起作用,那么这就是要走的路。 – mob 2010-10-01 15:22:08

7

重:为什么,你问?因为我想编写一个构建if(m /.../){} elsif(m /.../){}的链的动态例程...检查然后根据输入字符串调用动态函数。

喜欢你上述可以做,而不必诉诸eval创建如果& ELSIF链:

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! 

参考:上Dispatch Table维基百科条目。

/I3az/

4

你看到像CODE(0xDEADBEEF)当你打印一个子程序引用,因为这是怎样的Perl stringifies引用。你看,说诸如此类的事情,如果你打印任何类型的引用,不超载字串:

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

通常情况下,你真的不能使用任何价值的,你看到的数字不一定对应到一个真实的内存地址。

你在做什么,看看我在掌握Perl的动态子程序章节。我谈了很多关于编写子例程和使用匿名子例程的不同方法。一个模块,如Data::Constraint甚至可能会给你一些想法。我在How can a Perl force its caller to return?的回答中也谈到了这个问题。

相关问题