Переменная Perl по умолчанию $ _ - PullRequest
7 голосов
/ 22 августа 2010

Мне показалось, что я понял карту, однако следующее дает результаты, которые я не понимаю.Я знаю, почему это происходит, я просто не знаю, как это происходит.

Проблема в том, что содержимое @array изменяется, потому что $_ сбрасывается во время вызова _do_stuff_to_file.так что печатается here: \nhere:\n, когда я ожидаю, что оно будет here: donkie\nhere: kong\n.

Примечание: Это не проверенный код.Это именно то, что я помню из лаборатории.Почему меняется содержимое @array

Если я установил $_ на $f перед возвратом 1 из _some_func.Тогда массив все еще не поврежден.

Вот пример программы, иллюстрирующей то, что я вижу:

my @array = ("donkie", "kong");
map { push @junk, _some_func('blah', $_); } @array;

if (join ('', @junk) !~ /0/)
{   # for example sake this is always true since return 1 from _some_func.
    print map { "here: $_\n"; } @array;
}

sub _some_func
{   # for example sake, lets say $f always exists as a file.
    my $j = shift;
    my $f = shift;
    return 0 if !open(FILE, "< $f");
    close FILE;
    _do_stuff_to_file($f);

    return 1;
}


sub _do_stuff_to_file
{
    my $f = shift;
    open(IN, "< $f");
    open(OUT, "> $f.new");

    while (<IN>)
    {
        print OUT;
    }

    close IN;
    close OUT;
}

Ответы [ 4 ]

9 голосов
/ 22 августа 2010

Многие функции в Perl используют переменную по умолчанию $_. Среди них map и оператор readline <>. Как и foreach, map делает переменную цикла псевдонимом для каждого элемента списка, который она обрабатывает. Происходит то, что эта строка:

while (<IN>)

присваивает $_, пока действует псевдоним map. Это одна из проблем с использованием $_ (или любой другой глобальной переменной) - странное действие на расстоянии. Если вы собираетесь использовать $_, сначала локализуйте его:

local $_;
while (<IN>)
...

Вместо этого используйте вместо этого лексическую переменную:

while (my $line = <IN>)
4 голосов
/ 22 августа 2010

Большинство вещей, которые устанавливают $ _, неявно называют его псевдонимом, поэтому не вызовут этой проблемы;исключение составляет while (<filehandle>).Хотя вы можете локализовать $ _ (в идеале с my $_;), лучше просто никогда не допускать, пока неявно устанавливается $ _.Сделайте while ( my $line = <filehandle> ) вместо этого.(Специальная неявная определенная () все еще происходит.)

4 голосов
/ 22 августа 2010

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

my @array = ("donkie", "kong");
my @junk=map {_some_func('blah', $_) } @array;

if (join ('', @junk) !~ /0/)
{   # for example sake this is always true since return 1 from _some_func.
    print map { "here: $_\n"; } @array;
}

sub _some_func
{   # for example sake, lets say $f always exists as a file.
    my $j = shift;
    my $f = shift;
    return 0 if !-e $f;
    _do_stuff_to_file($f);
    return 1;
}


sub _do_stuff_to_file
{
    my $f = shift;
    local $_;
    open(IN, "<",$f);
    open(OUT, ">", "$f.new");

    while (<IN>)
    {
        print OUT;
    }

    close IN;
    close OUT;
}

PS map возвращает массив с тем же количеством элементов (если скаляр возвращается из блока).grep возвращает только элементы, для которых блок имеет значение true.

0 голосов
/ 22 августа 2010

Я одобряю ответы Александра и Михаила: _do_stuff_to_file() меняет значение $_. Поскольку в контексте map $_ это просто имя для хранения сопоставленного элемента, массив изменяется.

Александр и Майкл предлагают изменить _do_stuff_to_file(), чтобы они не влияли на значение $_. Это хорошая практика для локализации специальных переменных, таких как $_, чтобы избежать нарушения внешнего контекста.

Вот альтернативное решение, которое позволяет не касаться этой функции: «разорвать» ссылку внутри блока карты путем локализации перед вызовом функции:

map { my $x=$_; local $_; push @junk, _some_func('blah', $x); } @array;

или более в соответствии с общим стилем:

@junk = map { my $x=$_; local $_; _some_func('blah', $x) } @array;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...