Могу ли я вызвать подпрограмму по жестко закодированному адресу в Perl? - PullRequest
4 голосов
/ 01 октября 2010

Допустим, у меня есть следующий фрагмент кода:

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/.../ ) { } ... проверок, которые затем вызывают динамические функции в зависимости от входной строки.

Ответы [ 5 ]

14 голосов
/ 01 октября 2010

Вместо строковых значений вы можете использовать обычные лексические замыкания:

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

Также возможно получить ссылку на код по его адресу, но это было бы намного сложнее, сложнее для понимания и, как правило, не очень хорошая идея.

8 голосов
/ 01 октября 2010

Как насчет использования замыкания вместо строки eval?

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

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

Я уверен, что вы можете адаптировать это, чтобы сделать то, что вы также упомянули.

7 голосов
/ 01 октября 2010

re: почему, спросите вы? Потому что я хотел бы написать динамическую подпрограмму, которая создает цепочку проверок if (m /.../) {} elsif (m /.../) {} ..., которые затем вызывают динамические функции в зависимости от входной строки .

Создать цепочку if & elsif , как вы описали выше, можно без необходимости прибегать к 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: запись в Википедии Таблица отправки .

/ I3az /

4 голосов
/ 01 октября 2010

Когда вы печатаете ссылку на подпрограмму, вы видите что-то вроде CODE(0xDEADBEEF), потому что именно так Perl структурирует ссылки.Вы видите что-то вроде этого, если печатаете какую-либо ссылку, которая не перегружает строку:обязательно соответствует реальному адресу памяти.

Сведения о том, что вы делаете, см. в динамических главах подпрограмм, которые я имею в Mastering Perl .Я довольно много говорю о различных вещах, которые вы можете сделать, чтобы составлять подпрограммы и работать с анонимными подпрограммами.Модуль типа Data :: Constraint может даже дать вам несколько идей.Я также говорю об этом в своем ответе на Как Perl может заставить своего вызывающего возвращаться? .

1 голос
/ 01 октября 2010

Это может быть возможно (см. эту похожую проблему и решения ), но, возможно, есть и другой способ.Как насчет (глобального?) Хэша, который отображает ссылки на строковый код на фактические ссылки на код?

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
...