Изучение использования анонимных подписчиков - PullRequest
10 голосов
/ 30 июня 2011

Меня всегда смущало назначение и использование анонимных подписчиков в perl. Я понимаю концепцию, но ищу примеры и объяснения ценности этой практики.

Чтобы быть ясным:

sub foo { ... }   # <--- named sub
sub { ... }       # <--- anonymous sub

Например:

$ perl -e 'print sub { 1 }'
CODE(0xa4ab6c)

Сообщает, что sub возвращает скалярное значение. Итак, я могу сделать:

$ perl -e '$a = sub { 1 }; print $a'

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

Вопрос в том, как мне использовать эти сабвуферы? Зачем мне их использовать?

А для золотой звезды, есть ли проблема, которую можно решить только с помощью анонимного саба?

Ответы [ 8 ]

11 голосов
/ 30 июня 2011

Анонимные подпрограммы могут использоваться для всех видов вещей.

  1. Обратные вызовы для систем обработки событий:

    my $obj = Some::Obj->new;
    
    $obj->on_event(sub {...});
    
  2. Итераторы:

    sub stream {my $args = \@_; sub {shift @$args}}
    
    my $s = stream 1, 2, 3;
    
    say $s->(); # 1
    say $s->(); # 2 
    
  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'
    
  4. Создание псевдонимов:

    my $alias = sub {\@_}->(my $x, my $y);
    
    $alias[0]++;
    $alias[1] = 5;
    
    say "$x $y";  # '1 5''
    
  5. Динамическое программирование с замыканиями (например, создание набора подпрограмм, которые отличаются незначительно):

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

Не существует ситуации, когда анонимная подпрограмма может делать что-либо, чтоИменованная подпрограмма не может.Конструктор my $ref = sub {...} эквивалентен следующему:

sub throw_away_name {...}

my $ref = \&throw_away_name;

без необходимости выбора уникального 'throw_away_name' для каждой подпрограммы.

Эквивалентность также идет другим путем,sub name {...} эквивалентно:

 BEGIN {*name = sub {...}}

Таким образом, кроме имени, ссылка на код, созданная любым методом, одинакова.

Для вызова ссылки на подпрограмму вы можете использовать любойиз следующего:

 $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 
8 голосов
/ 30 июня 2011

Анонимные подпрограммы могут использоваться для создания замыканий.

Закрытие - это понятие из мира Lisp, которое гласит, что если вы определяете анонимную функцию в определенном лексическом контексте, она претендует на запуск в этом контексте, даже если она вызывается вне контекста.

8 голосов
/ 30 июня 2011

Вы можете использовать его для создания итераторов.

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
7 голосов
/ 30 июня 2011

Вот нечто похожее, что вы могли видеть раньше:

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

А также:

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

Ни один из этих анонимных подпрограмм, но их поведение может быть имитировано в ваших функцияханонимные подписчики.Им не нужно ключевое слово 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;
  }
}

Волшебство заставить его работать с $ _ немного здесь, чтобы написать здесь - вышеупомянутая версия работает только для подпрограмм, которые принимают аргументы.

4 голосов
/ 30 июня 2011

Я написал SAX-парсер для Perl, который управляется событиями.Вы можете передавать анонимные подписки на события начала / конца элемента.

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);
3 голосов
/ 30 июня 2011

Они обычно используются, когда вы хотите передать подпрограмму другому биту кода. Часто это случай «когда происходит X (в стороннем коде), делайте Y».

Например. При определении атрибута в Moose вы можете указать значение по умолчанию для этого атрибута, используя подпрограмму. Учитывая класс, который, как часть его определения:

  has 'size' => (
      is => 'ro',
      default =>
          sub { ( 'small', 'medium', 'large' )[ int( rand 3 ) ] },
      predicate => 'has_size',
  );

Всякий раз, когда экземпляр этого класса создается без передачи явного размера, вызывается sub, и возвращаемое значение будет размером для этого объекта.

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

var b = document.getElementById('my_button').
b.addEventListener('click', function (e) { alert('clicked!'); });
2 голосов
/ 30 июня 2011

Это сабы для ленивого программиста.Вы можете использовать их для локальных одноразовых функций и сохранить некоторые данные.Вместо

sub x { ... }
my $function_ptr = \&x;

теперь можно использовать

my $function_ptr = sub { ... };

Анонимные функции также являются закрытыми и доступны только через $function_ptr, поэтому они не имеют записив таблице символов.

2 голосов
/ 30 июня 2011

В вашем примере вы на самом деле не вызывали созданную подпрограмму.Вызов выполняется с использованием синтаксиса & $ a или $ a -> ().Что вы сделали, так это то, что вы сохранили ссылку на подпрограмму в $ a, затем ее преобразовали в строку и напечатали результат.Сравните:

my $a = sub {1};
my $b = sub {1};
print join("\n", $a, $a->(), $b, $b->());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...