探索匿名潜艇的用途

TLP*_*TLP 10 perl

我总是对perl中匿名subs的用途和用法感到有些困惑.我理解这个概念,但寻找这个实践的价值的例子和解释.

要明确:

sub foo { ... }   # <--- named sub
sub { ... }       # <--- anonymous sub
Run Code Online (Sandbox Code Playgroud)

例如:

$ perl -e 'print sub { 1 }'
CODE(0xa4ab6c)
Run Code Online (Sandbox Code Playgroud)

告诉我sub返回一个标量值.所以,我可以这样做:

$ perl -e '$a = sub { 1 }; print $a'
Run Code Online (Sandbox Code Playgroud)

对于与上面相同的输出.这当然适用于所有标量值,因此您可以使用匿名subs加载数组或散列.

问题是,我如何使用这些潜艇?我为什么要使用它们?

而对于一个金星来说,是否有任何问题只能用匿名子解决?

Eri*_*rom 11

匿名子程序可用于各种事物.

  1. 事件处理系统的回调:

    my $obj = Some::Obj->new;
    
    $obj->on_event(sub {...});
    
    Run Code Online (Sandbox Code Playgroud)
  2. 迭代器:

    sub stream {my $args = \@_; sub {shift @$args}}
    
    my $s = stream 1, 2, 3;
    
    say $s->(); # 1
    say $s->(); # 2 
    
    Run Code Online (Sandbox Code Playgroud)
  3. 高阶函数:

    sub apply (&@) {
        my $code = shift;
        $code->() for my @ret = @_;
        @ret
    }
    
    my @clean = apply {s/\W+/_/g} 'some string', 'another string.';
    
    say $clean[0]; #  'some_string'
    
    Run Code Online (Sandbox Code Playgroud)
  4. 创建别名数组:

    my $alias = sub {\@_}->(my $x, my $y);
    
    $alias[0]++;
    $alias[1] = 5;
    
    say "$x $y";  # '1 5''
    
    Run Code Online (Sandbox Code Playgroud)
  5. 使用闭包进行动态编程(例如创建一组仅相差很小的子程序):

    for my $name (qw(list of names)) {
        no strict 'refs';
        *$name = sub {... something_with($name) ...};
    }
    
    Run Code Online (Sandbox Code Playgroud)

没有匿名子例程可以执行命名子例程不能执行的任何操作的情况.该my $ref = sub {...}构造等同于以下内容:

sub throw_away_name {...}

my $ref = \&throw_away_name;
Run Code Online (Sandbox Code Playgroud)

无需为每个子选择唯一的'throw_away_name'而烦恼.

等价也是另一种方式,sub name {...}相当于:

 BEGIN {*name = sub {...}}
Run Code Online (Sandbox Code Playgroud)

除了名称之外,任一方法创建的代码引用都是相同的.

要调用子例程引用,可以使用以下任何一个:

 $code->();         # calls with no args
 $code->(1, 2, 3);  # calls with args (1, 2, 3)
 &$code();          # calls with no args
 &$code;            # calls with whatever @_ currently is
Run Code Online (Sandbox Code Playgroud)

您甚至可以使用代码引用作为有福或无法使用的标量的方法:

 my $list = sub {@{ $_[0] }};

 say for [1 .. 10]->$list  # which prints 1 .. 10 
Run Code Online (Sandbox Code Playgroud)


Oes*_*sor 8

您可以使用它来创建迭代器.

use strict;
use warnings;

use 5.012;

sub fib_it {
  my ($m, $n) = (0, 0);

  return sub {
    my $val = ( $m + $n );
    $val = 1 unless $val;
    ($m, $n) = ($n, $val);
    return $val;
  }
}

my $fibber = fib_it;
say $fibber->() for (1..3); ### 1 1 2

my $fibber2 = fib_it;
say $fibber2->() for (1..5); ### 1 1 2 3 5
say $fibber->() for (1..3); #### 3 5 8
Run Code Online (Sandbox Code Playgroud)


Eug*_*ash 8

匿名子例程可用于创建闭包.

Closure是一个Lisp世界的概念,它说如果你在一个特定的词汇上下文中定义一个匿名函数,它就假装在该上下文中运行,即使它在上下文之外被调用.


Chr*_*utz 7

以下是您可能见过的类似内容:

@new_list = map { $_ + 1 } @old_list;
Run Code Online (Sandbox Code Playgroud)

并且:

@sorted = sort { $a <=> $b } @unsorted;
Run Code Online (Sandbox Code Playgroud)

这些都不是匿名潜艇,但他们的行为可以在你的函数中使用匿名潜艇模仿.它们不需要sub关键字,因为函数(基本上)是原型,使它们的第一个参数成为子例程,而Perl认为这是一个特殊情况,sub可以将其关闭.(函数还会在调用您提供的子例程之前将必需变量设置为有意义的值,以简化参数传递,但这与之无关.)

你可以编写自己喜欢map的函数:

sub mapgrep (&@) { # make changes and also filter based on defined-ness
  my ($func, @list) = @_;
  my @new;
  for my $i (@list) {
    my $j = $func->($i);
    push @new, $j if defined $j;
  }
}
Run Code Online (Sandbox Code Playgroud)

让它与$ _一起工作的魔力在这里写得有点多 - 上面的版本仅适用于带参数的subs.