Perl 探索匿名sub的用法
我一直对perl中匿名sub的用途和用法感到困惑。我理解这一概念,但想找一些关于这一实践价值的例子和解释 要明确的是:Perl 探索匿名sub的用法,perl,Perl,我一直对perl中匿名sub的用途和用法感到困惑。我理解这一概念,但想找一些关于这一实践价值的例子和解释 要明确的是: sub foo { ... } # <--- named sub sub { ... } # <--- anonymous sub 告诉我sub返回一个标量值。因此,我可以做到: $ perl -e '$a = sub { 1 }; print $a' 用于与上述相同的输出。这当然适用于所有标量值,因此可以使用匿名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世界中的一个概念,它表示如果在特定词汇上下文中定义匿名函数,即使在上下文之外调用它,它也会假装在该上下文中运行
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