我可以在 Perl 中的硬编码地址处调用子例程吗?

发布于 2024-09-25 21:18:57 字数 927 浏览 0 评论 0原文

假设我有以下代码:

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/.../ ) { } ... 检查链,然后调用取决于输入字符串的动态函数。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

青春有你 2024-10-02 21:18:57

您可能希望使用常规的词法闭包而不是字符串评估:

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

也可以根据其地址检索代码引用,但这会更复杂,更难以理解,并且通常不是一个好主意。

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.

野味少女 2024-10-02 21:18:57

使用闭包而不是 eval 字符串怎么样?

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

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

我相信你也可以调整它来完成你提到的 elsif 事情。

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.

熟人话多 2024-10-02 21:18:57

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

创建一个if &像上面描述的 elsif 链可以完成,而不必求助于 eval< /code>

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!

参考:调度表上的维基百科条目。

/I3az/

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/

追风人 2024-10-02 21:18:57

当您打印子例程引用时,您会看到类似 CODE(0xDEADBEEF) 的内容,因为这就是 Perl 对引用进行字符串化的方式。如果您打印任何类型的不重载字符串化的引用,您会看到这样的事情:

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

通常您不能真正将该值用于任何用途,并且您看到的数字不一定对应于真实的内存地址。

对于您正在做的事情,请参阅我在掌握 Perl 中的动态子例程章节。我谈了很多关于编写子例程和使用匿名子例程可以做的不同事情。像 Data::Constraint 这样的模块甚至可能会给你一些想法。我还在 Perl 如何强制其调用者返回? 的回答中讨论了这一点。

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

初见 2024-10-02 21:18:57

也许有可能(请参阅这个类似的问题和解决方案),但也许还有另一种方法。将字符串化代码引用映射到实际代码引用的(全局?)哈希怎么样?

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文