Perl 探索匿名sub的用法

Perl 探索匿名sub的用法,perl,Perl,我一直对perl中匿名sub的用途和用法感到困惑。我理解这一概念,但想找一些关于这一实践价值的例子和解释 要明确的是: sub foo { ... } # <--- named sub sub { ... } # <--- anonymous sub 告诉我sub返回一个标量值。因此,我可以做到: $ perl -e '$a = sub { 1 }; print $a' 用于与上述相同的输出。这当然适用于所有标量值,因此可以使用匿名sub加载数组或散列 问题是,我

我一直对perl中匿名sub的用途和用法感到困惑。我理解这一概念,但想找一些关于这一实践价值的例子和解释

要明确的是:

sub foo { ... }   # <--- named sub
sub { ... }       # <--- anonymous sub
告诉我sub返回一个标量值。因此,我可以做到:

$ perl -e '$a = sub { 1 }; print $a'
用于与上述相同的输出。这当然适用于所有标量值,因此可以使用匿名sub加载数组或散列

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


对于金星来说,是否有任何问题只能通过匿名sub来解决?

当您想将sub传递给另一位代码时,通常会使用匿名sub。这通常是“当X发生时(在第三方代码中)执行Y”的情况

比如说。在中定义属性时,可以使用子类指定该属性的默认值。给定类的定义包括:

  has 'size' => (
      is => 'ro',
      default =>
          sub { ( 'small', 'medium', 'large' )[ int( rand 3 ) ] },
      predicate => 'has_size',
  );
每当在没有传递显式大小的情况下创建该类的实例时,将调用该子类,返回值将是该对象的大小

如果我们切换到另一种语言来给出一个不同的示例,您将在JavaScript中找到类似的概念

var b = document.getElementById('my_button').
b.addEventListener('click', function (e) { alert('clicked!'); });

我为perl编写了一个SAX解析器,它是事件驱动的。您可以将匿名sub传递给元素上的开始/结束事件

my $str = "<xml><row><data></data></row></xml>":

my $parser = SAXParser->new();

$parser->when('row')->begin(sub {
    my ($element) = @_;
    push(@rows, $row);
});

$parser->when('row')->end(sub {
   ## do something like serialize it or whatever
});

$parser->parse($str);
my$str=”“:
my$parser=SAXParser->new();
$parser->when('row')->开始(sub{
我的($元素)=@;
推送(@rows,$row);
});
$parser->when('row')->end(sub{
##做一些像序列化它之类的事情
});
$parser->parse($str);

您可以使用它创建迭代器

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

以下是您以前可能见过的类似情况:

@new_list = map { $_ + 1 } @old_list;
而且:

@sorted = sort { $a <=> $b } @unsorted;

这里要写的是使它与$一起工作的神奇之处——上面的版本只适用于带有参数的sub。

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

闭包是Lisp世界中的一个概念,它表示如果在特定词汇上下文中定义匿名函数,即使在上下文之外调用它,它也会假装在该上下文中运行


在您的示例中,您实际上没有调用创建的子例程。调用使用&$a或$a->()语法执行。您所做的是将对子例程的引用存储在$a中,然后将其字符串化并打印结果。比较:

my $a = sub {1};
my $b = sub {1};
print join("\n", $a, $a->(), $b, $b->());

这些是懒惰程序员的sub。您可以将它们用于本地丢弃函数,并可以节省一些输入。而不是

sub x { ... }
my $function_ptr = \&x;
您现在可以使用

my $function_ptr = sub { ... };

匿名函数也是私有的,只能通过
$function\u ptr
访问,因此它们在符号表中没有条目。

匿名子例程可用于各种用途

  • 事件处理系统的回调:

    my $obj = Some::Obj->new;
    
    $obj->on_event(sub {...});
    
  • 迭代器:

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

    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'
    
  • 创建别名数组:

    my $alias = sub {\@_}->(my $x, my $y);
    
    $alias[0]++;
    $alias[1] = 5;
    
    say "$x $y";  # '1 5''
    
  • 带闭包的动态规划(例如创建一组只相差一小部分的子例程):

  • 在任何情况下,匿名子例程都不能做命名子例程不能做的事情。
    my$ref=sub{…}
    构造函数等效于以下内容:

    sub throw_away_name {...}
    
    my $ref = \&throw_away_name;
    
     $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
    
    不必费心为每艘潜艇确定一个唯一的“废弃名称”

    等价性也反过来,子名称{…}等价于:

     BEGIN {*name = sub {...}}
    
    因此,除了名称之外,由任一方法创建的代码引用都是相同的

    要调用子例程引用,您可以使用以下任一选项:

    sub throw_away_name {...}
    
    my $ref = \&throw_away_name;
    
     $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
    
    您甚至可以将代码引用用作受祝福标量或非受祝福标量上的方法:

     my $list = sub {@{ $_[0] }};
    
     say for [1 .. 10]->$list  # which prints 1 .. 10 
    

    “对于一个金星来说,是否有任何问题只能通过匿名潜水艇来解决?”没有。我的金星在哪里p更多的例子,你可以摇杆说:@Chris,它在邮件中。如果第一个例子像一个匿名sub,添加一个
    返回将不会终止函数,只有
    映射
    。尝试sub-foo{my@old_list=(1..3);my@new_list=map{return$\+1}@old_list;打印“foo的结尾”;}foo();打印“结束\n”;“这两个都是匿名潜艇。”不,不是。参见hexcoder的评论。它们是
    eval
    ed的匿名代码块,没有被调用。我认为在那种上下文中(映射/排序),它们被称为块。我尝试在该上下文中使用sub关键字,但这是一个语法错误。@TLP-你说得对。
    perlsub
    页面列出了实现这一点的方法
    &
    ,甚至给出了
    sub-mygrep(&@)
    作为一种“编写自己的类似grep的函数”的方法,但后来注意到grep是不可重载的,可能是因为它具有前面提到的块语义
    &
    作为原型中的第一个字符允许您使用
    func{…}
    语法;但是,要使$(或$a和$b)按照类似grep函数的“预期”工作,还需要一些努力。请参见
    List::Util::reduce
    。您能提供一个示例吗?一个小的、正常命名的子例程也可以是闭包:
    {my$x;sub count{$x++}
    +1一个好的,但有点难以理解的解释和示例。:)谢谢。我今天遇到的一个警告似乎表明,匿名子例程可以处理一些命名子例程无法始终正确执行的事情:
    变量“%s”不会在…上保持共享状态。
     my $list = sub {@{ $_[0] }};
    
     say for [1 .. 10]->$list  # which prints 1 .. 10