2010-10-01 70 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?的回答中也談到了這個問題。

相關問題