Как использовать $a и $b в подпрограмме Perl

Я хотел бы использовать переменные $a и $b в моих анонимных двоичных функциях, как это делается в sort {$a <=> $b} (1, 2, 3), но я не могу понять, почему код, например

#!/usr/bin/env perl
use strict;
use warnings;

Foo::Bar(sub { $a + $b });

package Foo;
sub Bar {
    my ($function) = @_; 

    for my $i (1, 2, 3) {
        local ($a, $b) = ($i, $i);
        print $function->() . "\n";
    }
}    

не работает. Пока

#!/usr/bin/env perl
use strict;
use warnings;

Foo::Bar(sub { $_ });

package Foo;
sub Bar {
    my ($function) = @_; 

    for my $i (1, 2, 3) {
        local $_ = $i;
        print $function->() . "\n";
    }
}

отлично работает.

Что я делаю неправильно?

Ответ 1

$a и $b - специальные переменные пакета. Вы вызываете Foo::Bar из своего пакета main, поэтому вам нужно установить $main::a и $main::b, чтобы заставить его работать. Вы можете использовать caller, чтобы получить имя вызывающего пакета. Это должно работать:

#!/usr/bin/env perl
use strict;
use warnings;

Foo::Bar(sub { $a + $b });

package Foo;
sub Bar {
    my ($function) = @_; 
    my $pkg = caller;

    for my $i (1, 2, 3) {
        no strict 'refs';
        local *{ $pkg . '::a' } = \$i;
        local *{ $pkg . '::b' } = \$i;
        print $function->() . "\n";
    }
}    

Ответ 2

На всякий случай кого-то интересует, копия и вставка из List:: MoreUtils:: PP v.0.428 (с момента Декабрь 2017 года):

# begin copyrighted content
sub reduce_u(&@)
{
    my $code = shift;

    # Localise $a, $b
    my ($caller_a, $caller_b) = do
    {
        my $pkg = caller();
        no strict 'refs';
        \*{$pkg . '::a'}, \*{$pkg . '::b'};
    };

    local (*$caller_a, *$caller_b);
    *$caller_a = \();
    for (0 .. $#_)
    {
        *$caller_b = \$_[$_];
        *$caller_a = \($code->());
    }

    ${*$caller_a};
}
# end copyrighted content

Он отличается от образца выше в области, затронутой no strict 'refs';. Вероятно, это невозможно сделать без каких-либо ограничений.