Подтвердить что ты не робот

Почему я должен использовать анонимные подпрограммы Perl вместо имени?

Мне просто интересно, почему в Perl можно было бы использовать анонимную подпрограмму против названного. Спасибо.

4b9b3361

Ответ 1

  • Вы можете хранить анонимные субмарины в массивах, хешах и скалярах.
  • Вы можете создавать их во время выполнения
  • Вы можете передать их как аргументы другим функциям.
  • Вы можете сохранить переменные в окружающем пространстве.

Последняя точка, вероятно, самая важная, поскольку она часто является самым неожиданным аспектом именованных и анонимных подпрограмм в 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!

Но измените "inner" с именованной подпрограммы на ссылку на анонимную подпрограмму, и она работает гораздо менее удивительно:

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() показало бы "неожиданное" поведение, продемонстрированное ранее.

Ответ 2

Приходят на ум обратные вызовы и генераторы. Пример:

#!/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

Ответ 3

"Анонимные" подпрограммы действительно похожи на обычные, именованные подпрограммы, за исключением того, что они не привязаны к имени в таблице символов.

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'

Более подробно о том, почему полезны анонимные подпрограммы, я рекомендую книгу Perl более высокого порядка, которая описывает различные методы и приложения функционального программирования в Perl.

Ответ 4

Я говорю об анонимных подпрограммах и почему вы использовали их в Освоение Perl. Короче говоря, вы начинаете думать о поведении как о другой форме данных, так же, как вы думаете о строках или числах. Когда вам будет комфортно с этой идеей, вы можете сделать некоторые довольно удивительные вещи, потому что вы можете оттолкнуть множество решений до самого позднего времени в программе, и вам не нужно сужать свой дизайн кода, чтобы иметь дело заранее с любой ситуацией, которая может появиться.

Вы можете написать код, зная, что вы будете запускать подпрограмму, только вы еще не знаете, какой из них. Вы доверяете предыдущим шагам, чтобы работать для вас. Как только вы сможете это сделать, вы находитесь на другом уровне программирования, который чувствует, что вы пишете код, чтобы создать свою программу для вас. Некоторые проблемы программирования намного проще решить.

И, как и любая другая функция, вы можете использовать это слишком далеко или использовать ее ненадлежащим образом.

Ответ 5

Канонический ответ для анонимной подпрограммы обычно представляет собой числовую сортировку массива:

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

{ $a <=> $b } представляет анонимную подпрограмму.

Ответ 6

Во-первых: sub вещь - суб. 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].

Ответ 7

Вот пример из моего переписать Nasm 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"
    );
  }
);

В принципе, единственная причина, по которой я могу думать, - это для calbacks или для таблицы перехода.