Какой смысл использовать vars в этой подпрограмме Perl?

В одной из глав Освоение Perl, brian d foy показывает этот фрагмент из List:: Util:

sub reduce(&@) {
    my $code = shift;
    no strict "refs";
    return shift unless @_ > 1;
    use vars qw($a $b);
    my $caller = caller;
    local(*{$caller . "::a"}) = \my $a;
    local(*{$caller . "::b"}) = \my $b;
    $a = shift;
    foreach(@_) {
        $b = $_;
        $a = &{$code}();
    }
    $a;
}

Я не понимаю, что это за точка строки use vars qw($a $b). Даже если я прокомментирую это, я получаю тот же вывод и предупреждения.

Ответ 1

Это делается потому, что List:: Util использует функцию reduce() внутри.

В период отсутствия use vars при использовании функции появляется следующее предупреждение:

Name "List::MyUtil::a" used only once: possible typo at a.pl line 35.
Name "List::MyUtil::b" used only once: possible typo at a.pl line 35.

Вы можете убедиться в этом, выполнив следующий код:

use strict;
use warnings;

package List::MyUtil;

sub reduce (&@) {
   # INSERT THE TEXT FROM SUBROUTINE HERE - deleted to save space in the answer
}

sub x {
    return reduce(sub {$a+$b}, 1,2,3);
}

package main;
my $res = List::MyUtil::x();
print "$res\n";

И затем снова запустите его с отключенным use vars.

Ответ 2

Как отмечает DVK, если мы запустим код с use vars закомментированным, мы получим предупреждение о том, что переменные используются только один раз.

Другой способ подавить предупреждение - на стороне вызывающего абонента, то есть в вызове reduce, а не в функции reduce. Это нужно делать при использовании функций из List::Util или List::MoreUtils, которые берут ссылки на код (например, pairwise). Оба этих подхода работают на стороне вызывающего абонента:

my @sums = pairwise { no warnings 'once'; $a + $b } @x, @y;

my @sums = pairwise { our($a, $b);        $a + $b } @x, @y;

Ответ 3

Из параграфа сразу после этого кода это объясняется. Там сочетание пакетных и лексических переменных в той же области:

Остальная часть сокращения работает подобно сортировке, помещая два элемента в переменные пакета $a и $b. Грэм определяет лексические переменные с этими именами и сразу же присваивает typeglobs для $a и $b в вызывающем пакете, используя символические ссылки. После этого значения $a и $b являются лексическими версиями. Когда он вызывает аргумент подпрограммы & {$ code}(), этот код просматривает свои переменные пакета, которые действуют, когда я написал подпрограмму. Понял? Внутри уменьшить, я использую лексические версии, но внутри $code я использую версии пакета из вызывающего пакета. Вот почему Грэм сделал их псевдонимами друг друга.