什么语法(如果有的话)能够引用像shift
这样的内置函数?
$shift_ref = $your_magic_syntax_here;
您可以使用与用户定义的子相同的方式:
sub test { ... }
$test_ref = \&test;
我尝试过以下操作,但都不起作用:
\&shift
\&CORE::shift
\&{'shift'}
\&{'CORE::shift'}
如果需要,你的答案可以包括XS,但我不愿意。
澄清:我正在寻找一种通用解决方案,可以从任何内置程序获得功能完备的代码。然后可以将此coderef传递给任何更高阶的函数,就像对用户定义的子的引用一样。到目前为止似乎是共识,这是不可能的,任何人都不同意?
答案 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)
答案 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。