你能在Pe​​rl中引用内置函数吗?

时间:2009-10-18 17:54:31

标签: perl syntax

什么语法(如果有的话)能够引用像shift这样的内置函数?

$shift_ref = $your_magic_syntax_here;

您可以使用与用户定义的子相同的方式:

sub test { ... }

$test_ref = \&test;

我尝试过以下操作,但都不起作用:

\&shift
\&CORE::shift
\&{'shift'}
\&{'CORE::shift'}

如果需要,你的答案可以包括XS,但我不愿意。

澄清:我正在寻找一种通用解决方案,可以从任何内置程序获得功能完备的代码。然后可以将此coderef传递给任何更高阶的函数,就像对用户定义的子的引用一样。到目前为止似乎是共识,这是不可能的,任何人都不同意?

7 个答案:

答案 0 :(得分:4)

不,你不能。您试图解决的根本问题是什么?可能有某种方法可以做任何事情。

问题的补充部分“如果需要,你的答案可以包括XS,但我不愿意。”, 从XS调用builtins非常困难,因为内置函数设置为假设它们作为已编译的optree的一部分运行并设置了一些全局变量。通常,调用内置程序本身使用的一些底层函数要容易得多,尽管并不总是有这样的函数,所以你会看到如下内容:

buffer = sv_2mortal(newSVpvf("(caller(%d))[3]", (int) frame));
caller = eval_pv(SvPV_nolen(buffer), 1);

(从XS执行字符串eval,而不是通过直接调用pp_caller所需的箍)。

答案 1 :(得分:4)

我正在玩这个通用解决方案,并使用eval提出了以下肮脏的黑客攻击。它基本上使用原型来拉开@_然后调用内置函数。这只是经过了轻微的测试,并使用了eval的字符串形式,所以有些人可能会说它已经坏了: - )

use 5.10.0;
use strict;
use warnings;

sub builtin {
    my ($sub, $my, $id) = ($_[0], '');
    my $proto = prototype $sub         //
                prototype "CORE::$sub" //
                $_[1]                  //
                ($sub =~ /map|grep/ ? '&@' : '@;_');
    for ($proto =~ /(\\?.)/g) { $id++;
        if (/(?|(\$|&)|.(.))/) {
            $my  .= "my \$_$id = shift;";
            $sub .= " $1\$_$id,";
        } elsif (/([@%])/) {
            $my  .= "my $1_$id = splice \@_, 0, \@_;";
            $sub .= " $1_$id,";
        } elsif (/_/) {
            $my  .= "my \$_$id = \@_ ? shift : \$_;";
            $sub .= " \$_$id,"
        }
    }
    eval "sub ($proto) {$my $sub}"
        or die "prototype ($proto) failed for '$_[0]', ".
               "try passing a prototype string as \$_[1]"
}

my $shift = builtin 'shift';
my @a = 1..10;
say $shift->(\@a);
say "@a";

my $uc = builtin 'uc';
local $_ = 'goodbye';
say $uc->('hello '), &$uc;

my $time = builtin 'time';
say &$time;

my $map = builtin 'map';
my $reverse = builtin 'reverse';
say $map->(sub{"$_, "}, $reverse->(@a));

my %h = (a=>1, b=>2);
my $keys = builtin 'keys';
say $keys->(\%h);

# which prints
# 1
# 2 3 4 5 6 7 8 9 10
# HELLO GOODBYE
# 1256088298
# 10, 9, 8, 7, 6, 5, 4, 3, 2, 
# ab

修改如下并重构。

答案 2 :(得分:1)

如果先修补内部方法(这会给你补丁的coderef),你可以这样做:

use strict;
use warnings;

BEGIN {
    *CORE::GLOBAL::die = sub { warn "patched die: '$_[0]'"; exit 3 };
}

print "ref to patched die: " . \&CORE::GLOBAL::die . "\n";
die "ack, I am slain";

给出输出:

ref to patched die: CODE(0x1801060)
patched die: 'ack, I am slain' at patch.pl line 5.

BTW:如果有人能解释为什么需要以*CORE::GLOBAL::die而不是*CORE::die来完成覆盖,我将不胜感激。我找不到任何参考资料。另外,为什么必须在BEGIN块中完成覆盖? die()调用是在运行时完成的,为什么不能在之前的运行时完成覆盖?

答案 3 :(得分:1)

你可以用你可以引用的东西包裹移位,但你必须使用原型来使用它,因为移位是特殊的。

sub my_shift (\@) { my $ll = shift; return shift @$ll }

问题是原型系统无法神奇地弄清楚当它在标量中调用一些随机的ref-to-sub时,它需要在调用子例程之前接受引用。

my @list = (1,2,3,4);

sub my_shift (\@) { my $ll = shift; return shift @$ll }

my $a = shift @list;
my $my_shift_ref = \&my_shift;
my $b = (&{$my_shift_ref}  (\@list) ); # see below

print "a=$a, b=$b\n";

for (my $i = 0; $i <= $#list; ++$i) { print "\$list[$i] = ",$list[$i],"\n"; }

如果只调用@list perl barfs,因为它无法像shift那样自动生成引用。

另见:[http://www.perl.com/language/misc/fmproto.html][Tom Christensen的文章]。

当然,对于不像转移这样特殊的内置运算,你总能做到

sub my_fork { return fork; }

然后&my_fork你想要的一切。

答案 4 :(得分:1)

据我所知,您希望在某些数据上调用coderef,并且它可能指向您的某些函数或内置函数。

如果我是对的,只需把内置物关闭:

#!/usr/bin/perl -w
use strict;

my $coderef = \&test;
$coderef->( "Test %u\n", 1 );

$coderef = sub { printf @_ };
$coderef->( "Test %u\n", 2 );

exit;

sub test {
    print join(' ', map { "[$_]" } @_) . "\n";
}

也可以使用shift进行操作,但请记住,没有显式数组工作的移位可以根据调用的位置在不同的数组上工作。

答案 5 :(得分:1)

如果您想了解在生产质量代码中伪造它需要什么,请查看autodie的代码。肉在Fatal。如果你是一个疯狂的海盗绝地澳大利亚人,请帮助。

答案 6 :(得分:0)

我能让它发挥作用的唯一方法是引用sub{shift}

perl -e '@a=(1..3); $f=sub{shift}; print($f->(@a), "\n");'

这在功能上等同于:

perl -e '@a=(1..3); print(shift(@a), "\n");'

这可能只是perl -e 'print 1, "\n"'但我们不会谈论内置。

对于你的信息,我很惊讶,一个人不能引用内置,现在我已经清楚地告诉我,我不禁将其视为Perl的缺陷。

更新 Eric正确地指出$f=sub{shift}; $f->(@a)保持@a不变。它应该更像是:

perl -e '@a=(1..3); $f=sub{shift @{+shift}}; print($f->(\@a), "\n");

感谢Eric。