为什么我要使用 Perl 匿名子例程而不是命名子例程?

发布于 2024-07-18 17:45:27 字数 49 浏览 9 评论 0原文

我只是好奇为什么人们会选择在 Perl 中使用匿名子例程而不是命名子例程。 谢谢。

I'm just curious why one would choose to use an anonymous subroutine, versus a named one, in Perl. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

狠疯拽 2024-07-25 17:45:27
  • 您可以将匿名子存储在数组、散列和标量中。
  • 您可以在运行时构建它们,
  • 也可以将它们作为参数传递给其他函数。
  • 您可以将变量保留在周围范围内。

最后一点可能是最重要的,因为它通常是 Perl 中命名子例程与匿名子例程最意想不到的方面。 示例:

sub outer
{
  my $a = 123;

  sub inner
  {
    print $a, "\n";
  }

  # At this point, $a is 123, so this call should always print 123, right?
  inner();

  $a = 456;
}

outer(); # prints 123
outer(); # prints 456! Surprise!

但是将“内部”从命名子例程更改为对匿名子例程的引用,它的工作方式就不那么令人惊讶了:(

sub outer
{
  my $a = 123;

  my $inner = sub
  {
    print $a, "\n";
  };

  # At this point, $a is 123, and since the anonymous subrotine 
  # whose reference is stored in $inner closes over $a in the 
  # "expected" way...
  $inner->();

  $a = 456;
}

# ...we see the "expected" results
outer(); # prints 123
outer(); # prints 123

当然,每个人的期望都不同,因此“预期”周围的“恐吓引号”。)

这是实际代码中的使用示例(尽管应该注意的是,File::Find 接口通常被认为是一个很差的接口,因为它使用了全局变量,而不是使用了匿名子例程) :

sub find_files
{
  my @files;

  my $wanted = sub
  { 
    if($something)
    {
      push @files, $File::Find::name;
    }
  };

  # The find() function called here is imported from File::Find
  find({ wanted => $wanted }, $directory);

  return @files;
}

将命名子例程作为 wanted 参数的值传递将需要使用只能使用一次的例程污染命名空间,并在内定义命名子例程>find_files() 子例程将表现出前面演示的“意外”行为。

  • You can store anonymous subs in arrays, hashes and scalars.
  • You can build them at runtime
  • You can pass them as arguments to other functions.
  • You get to keep variables in the surrounding scope.

The last point is probably the most important, because it's often the most unexpected facet of named vs. anonymous subroutines in Perl. Example:

sub outer
{
  my $a = 123;

  sub inner
  {
    print $a, "\n";
  }

  # At this point, $a is 123, so this call should always print 123, right?
  inner();

  $a = 456;
}

outer(); # prints 123
outer(); # prints 456! Surprise!

But change "inner" from a named subroutine to a reference to an anonymous subroutine and it works is a much less surprising manner:

sub outer
{
  my $a = 123;

  my $inner = sub
  {
    print $a, "\n";
  };

  # At this point, $a is 123, and since the anonymous subrotine 
  # whose reference is stored in $inner closes over $a in the 
  # "expected" way...
  $inner->();

  $a = 456;
}

# ...we see the "expected" results
outer(); # prints 123
outer(); # prints 123

(Of course, everyone's expectations are different, thus the "scare quotes" around "expected.")

Here's an example use in real code (though it should be noted that the File::Find interface is generally considered to be a poor one—due to its use of global variables, not its use of anonymous subroutines):

sub find_files
{
  my @files;

  my $wanted = sub
  { 
    if($something)
    {
      push @files, $File::Find::name;
    }
  };

  # The find() function called here is imported from File::Find
  find({ wanted => $wanted }, $directory);

  return @files;
}

Passing a named subroutine as the value of the wanted parameter would require polluting the namespace with a routine that may only be used once, and defining a named subroutine within the find_files() subroutine would exhibit the "unexpected" behavior demonstrated earlier.

我很坚强 2024-07-25 17:45:27

我想到了回调和生成器。 一个例子:

#!/usr/bin/perl

use strict;
use warnings;

sub generate_multiplier {
    my ($coef) = @_;

    return sub { 
        my ($val) = @_;
        $coef * $val;
    }
}

my $doubler = generate_multiplier(2);
my $tripler = generate_multiplier(3);

for my $i ( 1 .. 10 ) {
    printf "%4d%4d%4d\n", $i, $doubler->($i), $tripler->($i);
}

__END__

C:\Temp> v
    1   2   3
    2   4   6
    3   6   9
    4   8  12
    5  10  15
    6  12  18
    7  14  21
    8  16  24
    9  18  27
   10  20  30

Callbacks and generators come to mind. An example:

#!/usr/bin/perl

use strict;
use warnings;

sub generate_multiplier {
    my ($coef) = @_;

    return sub { 
        my ($val) = @_;
        $coef * $val;
    }
}

my $doubler = generate_multiplier(2);
my $tripler = generate_multiplier(3);

for my $i ( 1 .. 10 ) {
    printf "%4d%4d%4d\n", $i, $doubler->($i), $tripler->($i);
}

__END__

C:\Temp> v
    1   2   3
    2   4   6
    3   6   9
    4   8  12
    5  10  15
    6  12  18
    7  14  21
    8  16  24
    9  18  27
   10  20  30
总以为 2024-07-25 17:45:27

“匿名”子例程与常规的命名子例程非常相似,只是它们不绑定到符号表中的名称。

sub Foo { stuff() }

BEGIN { *Foo = sub { stuff() } }  # essentially equivalent

在第二种情况下,创建“匿名”子例程,然后将其绑定到当前命名空间中的名称“Foo”。 BEGIN 块使其在编译时发生,就像处理命名子例程一样。 (这有点复杂,因为第一种情况给它一个将显示在堆栈跟踪中的名称。)

每当您想在运行时创建函数时,匿名子例程都很有用。 这对于“闭包”特别有用——“记住”其词法上下文的函数。 例如,将列表转换为迭代器:

use 5.010;
use strict;
use warnings;

sub make_iterator {
  my @list = @_;
  return sub { shift @list }; # new sub that 'remembers' @list
}

my $iter1 = make_iterator( 0 .. 10 ); 
my $iter2 = make_iterator( 'a' .. 'z' );

say $iter1->();  # '0'
say $iter1->();  # '1'
say $iter2->();  # 'a'

有关为什么匿名子例程有用的更多信息,我推荐这本书 Higher Order Perl 描述了 Perl 中函数式编程的各种技术和应用。

"Anonymous" subroutines are really similar to regular, named subroutines except that they aren't bound to a name in the symbol table.

sub Foo { stuff() }

BEGIN { *Foo = sub { stuff() } }  # essentially equivalent

In the second case, the "anonymous" subroutine is created and then bound to the name "Foo" in the current namespace. The BEGIN block makes it happen at compile time, just like how a named subroutine is treated. (It's a little more complicated in that the first case gives it a name that will show up in a stack trace.)

Anonymous subroutines are useful any time you want to create a function at run-time. This is particularly good for "closures" -- functions that "remember" their lexical context. For example, turning a list into an iterator:

use 5.010;
use strict;
use warnings;

sub make_iterator {
  my @list = @_;
  return sub { shift @list }; # new sub that 'remembers' @list
}

my $iter1 = make_iterator( 0 .. 10 ); 
my $iter2 = make_iterator( 'a' .. 'z' );

say $iter1->();  # '0'
say $iter1->();  # '1'
say $iter2->();  # 'a'

For a lot more on why anonymous subroutines are useful, I recommend the book Higher Order Perl which describes various techniques and applications of functional programming in Perl.

羞稚 2024-07-25 17:45:27

我在掌握 Perl 中讨论了匿名子例程以及为什么要使用它们。 简而言之,您开始将行为视为另一种形式的数据,就像您考虑字符串或数字一样。 当您对这个想法感到满意时,您可以做一些非常令人惊奇的事情,因为您可以将许多决策推迟到程序的最后阶段,并且您不必扭曲代码设计来提前处理每种情况可能会出现。

您可以在知道要运行子例程的情况下编写代码,只是您还不知道是哪个子例程。 您相信前面的步骤可以为您解决这个问题。 一旦你能做到这一点,你就进入了编程的另一个层次,感觉就像你正在编写代码来为你创建程序一样。 通过这种方式,一些编程问题变得更容易解决。

而且,与其他所有功能一样,您可能会过度使用它或使用不当。

I talk about anonymous subroutines and why you would use them in Mastering Perl. In short, you start thinking about behavior as just another form of data just like you think about strings or numbers. When you are comfortable with that idea, you can do some pretty amazing things because you can push off a lot of decisions to very late in the program and you don't have to contort your code design to deal ahead of time with every situation that might show up.

You can write code knowing that you are going to run a subroutine, only you don't know which one yet. You trust the previous steps to work that out for you. Once you can do that, you're on another level of programming that feels like you're writing code to create your program for you. Some programming problems become much easier to solve this way.

And, like every other feature, you can take this too far or use it inappropriately.

绳情 2024-07-25 17:45:27

匿名子例程的规范答案通常是数组的数字排序:

my @sorted_array = sort { $a <=> $b } @array;

{ $a <=>; $b } 代表匿名子例程。

The canonical answer for anonymous subroutine is usually the numeric sorting of an array :

my @sorted_array = sort { $a <=> $b } @array;

The { $a <=> $b } represents an anonymous subroutine.

她如夕阳 2024-07-25 17:45:27

第一:子事物就是子事物。 my $thing=sub... 是存储在变量中的子引用。

第二:有一个微妙的用法差异:

use strict;
use warnings;

sub xx {
  my $zz=1;

   sub yy {
      print $zz;
   }
}


perl tmp.pl
Variable "$zz" will not stay shared at tmp.pl line 8.

将 [sub yy...] 更改为 [my $yy = sub {...] 或 [local *yy = sub{...] 并且投诉消失了。

另外,说实话,对 subs 的引用更容易处理,就像 @x=(1,2,3) 与 $x=[1, 2, 3] 一样。

First: sub thing is a sub. my $thing=sub... is a sub reference stored in a variable.

Second: There's a subtle usage difference:

use strict;
use warnings;

sub xx {
  my $zz=1;

   sub yy {
      print $zz;
   }
}


perl tmp.pl
Variable "$zz" will not stay shared at tmp.pl line 8.

Change [sub yy...] to [my $yy = sub {...] or [local *yy = sub{...] and the complaint goes away.

Also, to be honest, references-to-subs are just easier to deal with, much the same as @x=(1,2,3) versus $x=[1, 2, 3].

醉梦枕江山 2024-07-25 17:45:27

这是我的 重写的示例Nasm's version.pl

# jump table to subroutines / variables
my %jump = (
  id     => 'id',
  xid    => 'xid',
  hex_id => 'xid',

  h      => \&h,
  mac    => \&mac,
  sed    => \&sed,
  make   => \&make,
  nsis   => \&nsis,

  perl   => \&perl,
  dump   => \&perl,
  yaml   => \&yaml,
  yml    => \&yaml,
  json   => \&json,
  js     => \&json,

  help   => \&help,
  usage  => sub{
    require Pod::Usage;

    Pod::Usage::pod2usage(
      "run perldoc $0 or pod2text $0 for more information"
    );
  }
);

基本上我能想到的唯一原因是为了回调,或者为了跳转表。

Here's an example from my rewrite of Nasm's version.pl

# jump table to subroutines / variables
my %jump = (
  id     => 'id',
  xid    => 'xid',
  hex_id => 'xid',

  h      => \&h,
  mac    => \&mac,
  sed    => \&sed,
  make   => \&make,
  nsis   => \&nsis,

  perl   => \&perl,
  dump   => \&perl,
  yaml   => \&yaml,
  yml    => \&yaml,
  json   => \&json,
  js     => \&json,

  help   => \&help,
  usage  => sub{
    require Pod::Usage;

    Pod::Usage::pod2usage(
      "run perldoc $0 or pod2text $0 for more information"
    );
  }
);

Basically the only reason I can think of is for calbacks, or for a jump table.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文