Как использовать local * для ссылочных аргументов?

#perl #scope

#perl #область видимости

Вопрос:

Этот вопрос связан с последним пунктом пункта 46 в эффективном программировании на Perl.

Я протестировал эту функцию, которая позволяет передавать ссылки на массивы, но получать к ним доступ как к локальным массивам:

 use strict;
sub max_v_local {
    local ( *a, *b ) = @_;
    my $n = @a > @b ? @a : @b;
    my @resu<
    for ( my $i = 0 ; $i < $n ; $i   ) {
        push @result, $a[$i] > $b[$i] ? $a[$i] : $b[$i];
    }
    @resu<
}
  

Но я получил следующие ошибки, если я не использую strict :

 Variable "@a" is not imported
Variable "@b" is not imported
Global symbol "@a" requires explicit package name
Global symbol "@b" requires explicit package name
  

Есть ли способ сделать это с strict ?

Обновить

Немного дополнительной информации. Вышеупомянутая подпрограмма была уточнением того, что следует. Подпрограмма принимает 2 ссылки на массивы, но использование ссылок на массивы в подпрограмме может привести к беспорядку. Приведенный выше код, вероятно, будет быстрее и удобочитаем, поскольку он позволяет вам обращаться к arrayrefs как к локальным массивам.

 sub max_v {
    my ( $a, $b ) = @_;
    my $n = @$a > @$b ? @$a : @$b; # no. of items
    my @resu<
    for ( my $i = 0 ; $i < $n ; $i   ) {
        push @result, $$a[$i] > $$b[$i] ? $$a[$i] : $$b[$i];
    }
    @resu<
}
  

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

Комментарии:

1. Вы понимаете, что это архаичная конструкция в Perl, с незапамятных времен, когда не было ссылок и лексических переменных?

2. нет, я этого не понимал. Действительно ли это? Однако книга вышла в 2010 году. Я расскажу больше о том, почему я хотел заставить это работать.

3. «может запутаться»: Нет. Тривиально больше символов, но вряд ли беспорядочно.

Ответ №1:

ДА. Объявляйте их с помощью нашего:

 use strict;
sub max_v_local {
    local ( *x, *y ) = @_;
    our (@x, @y);
    my $n = @x > @y ? @x : @y;
    my @resu<
    for ( my $i = 0 ; $i < $n ; $i   ) {
        push @result, $x[$i] > $y[$i] ? $x[$i] : $y[$i];
    }
    @resu<
}
  

(Обычно не рекомендуется использовать переменные с именами a или b для чего-либо, кроме sort .)

Комментарии:

1. да, я заметил проблему с использованием a и b , но это код прямо из книги. Фактически, a и b используются на протяжении всей книги.

Ответ №2:

Как упоминает cjm, вам нужно объявлять переменные с our (или использовать полные имена).

Теперь несколько советов. Во-первых, непроверенное назначение из @_ в глобус — это своего рода авантюра. Я бы написал строку так:

 our (@a, @b);
local (*a, *b) = map @$_ => @_;
  

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

 ref eq 'ARRAY' or die "..." for @_;
our (@a, @b);
local (*a, *b) = @_;
  

Ответ №3:

Вы хотите использовать переменные пакета @a и @b , поэтому вы хотите использовать наши. Это почти для каждой переменной no strict "vars"; , и она имеет лексическую область.

 sub max_v_local {
    local ( *a, *b ) = @_;
    our ( @a, @b );
    ...
}
  

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

 sub max_v_local {
    local ( *a, *b ) = @_;
    our ( @a, @b );
    my $n = @a < @b ? @a : @b;
    return
       ( map { $a[$_] > $b[$_] ? $a[$_] : $b[$_] } 0..$n-1 ),
       @a[ @b .. $#a ],
       @b[ @a .. $#b ];
}
  

Кроме того, странно принимать ссылки и возвращать список. Возможно, вы захотите вернуть ссылку на массив. ( return [ ... ]; )

Ответ №4:

Думаю, это может быть потому, что вы явно не объявили свои переменные @a и @b