如何在Perl中运行匿名函数?

ask*_*ker 29 perl

(sub {
print 1;
})();



sub {
print 1;
}();
Run Code Online (Sandbox Code Playgroud)

我试过各种各样的方法,都错了......

zel*_*lio 38

(sub { ... }) 将为您提供指向该函数的指针,因此您必须通过引用调用.

(sub { print "Hello world\n" })->();

Blagovest Buyukliev指出的另一个简单方法是取消引用函数指针并使用{ }运算符调用它

&{ sub { print "Hello World" }}();


Egg*_*ung 26

是的,我没想到你们会想出那么多的可能性.但你是对的,这是perl和TIMTOWTDI:为creativitiy +1!

但说实话,我使用的形式几乎不是以下形式:

基本语法

my $greet = sub {
    my ( $name ) = @_;
    print "Hello $name\n";
};

# ...

$greet->( 'asker' )
Run Code Online (Sandbox Code Playgroud)

它非常简单:sub {}返回对子例程的引用,您可以像任何其他标量一样存储和传递子例程.您可以通过解除引用来调用它.还有第二种解除引用的语法:&{ $sub }( 'asker' )但我个人更喜欢箭头语法,因为我发现它更具可读性,并且几乎与解除引用哈希$hash->{ $key }和数组一致$array->[ $index ].有关参考文献的更多信息可以在perldoc perlref中找到.

我认为其他给出的例子有点高级,但为什么不看看它们:

sub bar {goto $foo};
bar;
Run Code Online (Sandbox Code Playgroud)

这些天很少见,也很害怕.但至少它是一个goto &function,被认为比它的歪歪扭扭的朋友更有害:goto LABEL或者goto EXPRESSION(自5.12起就被弃用并发出警告).实际上有些情况下,当你想使用那个表格时,因为这不是通常的函数调用.调用函数(bar在给定的示例中)不会出现在调用堆栈中.并且您没有传递参数,但@_会使用当前的参数.看看这个:

use Carp qw( cluck );

my $cluck = sub {
    my ( $message ) = @_;
    cluck $message . "\n";
};


sub invisible {
    @_ = ( 'fake' );
    goto $cluck;
}

invisible( 'real' );
Run Code Online (Sandbox Code Playgroud)

输出:

fake at bar.pl line 5
    main::__ANON__('fake') called at bar.pl line 14
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪中没有隐藏功能的提示.关于goto in perldoc -f goto的更多信息.

方法调用

''->$foo;
# or
undef->$foo;
Run Code Online (Sandbox Code Playgroud)

如果在对象上调用方法,则传递给该方法的第一个参数将是调用者(通常是实例或类名).我已经说过TIMTOWTCallAFunction了吗?

# this is just a normal named sub
sub ask {
    my ( $name, $question ) = @_;
    print "$question, $name?\n";
};

my $ask = \&ask; # lets take a reference to that sub 

my $question = "What's up";

'asker'->ask( $question ); # 1: doesn't work

my $meth_name = 'ask';
'asker'->$meth_name( $question ); # 2: doesn't work either

'asker'->$ask( $question ); # 1: this works
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中有两个调用,这些调用无效,因为perl将尝试查找ask在包中调用的方法asker(实际上,如果该代码位于所述包中,它将起作用).但第三个成功,因为你已经给perl正确的方法,它不需要搜索它.一如既往:perldoc中的更多信息我现在找不到任何理由,请原谅生产代码.

结论

最初我并不打算写那么多,但我认为在答案的开头有一个共同的解决方案,并对不寻常的结构有一些解释是很重要的.我承认在这里有点自私:我们每个人都可能最终维护某些代码,他们发现了这个问题并且只是复制了最顶层的例子.

  • +1为勇气; 每当我把时间都花在写这样的答案上时,卡车就会被包含......太长时间的卡车所淹没......或者......没有人想花这么多时间来读这个...所以,我会把在此+1中,以防你被卡车撞到.(以及其他任何人这样做过) (4认同)
  • @osirisgothra谢谢你的好话.答案已接近四年 - 到目前为止还没有卡车:-D (2认同)
  • 那些不能被阅读的程序员在糟糕的方式中懒惰,而不是好方法.他们基本上离"为我做我的[家]工作不远,所以我可以[到海边|家里早点]"人群. (2认同)

Eri*_*rom 12

在Perl中没有太多需要调用定义它的匿名子例程.通常,您可以使用裸块实现任何类型的范围.想到的一个用例是创建一个别名数组:

my $alias = sub {\@_}->(my ($x, $y, $z));

$x = $z = 0;
$y = 1;

print "@$alias"; # '0 1 0'
Run Code Online (Sandbox Code Playgroud)

否则,您通常会在变量或数据结构中存储匿名子例程.以下调用样式适用于变量和sub {...}声明:

dereference arrow:  sub {...}->(args)  or  $code->(args)

dereference sigil:  &{sub {...}}(args) or &$code(args)
Run Code Online (Sandbox Code Playgroud)

如果你在标量中有coderef,你也可以将它用作常规和祝福值的方法.

my $method = sub {...};

$obj->$method           # same as $method->($obj)
$obj->$method(...)      # $method->($obj, ...)

[1, 2, 3]->$method      # $method->([1, 2, 3])
[1, 2, 3]->$method(...) # $method->([1, 2, 3], ...)
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 5

通过寻找调用匿名函数的方法,我无休止地被逗乐:

$foo = sub {say 1};

sub bar {goto $foo};
bar;

''->$foo; # technically a method, along with the lovely:

undef->$foo;

() = sort $foo 1,1; # if you have only two arguments
Run Code Online (Sandbox Code Playgroud)

当然,显而易见的是:

&$foo();
$foo->();
Run Code Online (Sandbox Code Playgroud)