Perach и изменение переменной цикла - PullRequest
15 голосов
/ 10 июля 2010

Я пишу сценарий на Perl и у меня есть вопрос о конструкции foreach в Perl.

Похоже, что если вы измените одну из переменных цикла, она изменится в фактическом массиве. Это на самом деле так, или я что-то сделал полностью неправильно?

Я хочу изменить строку вроде abc.abc#a на abc_abc_a (подчеркивание не буквенно-цифровых символов), но мне нужно сохранить исходное значение в массиве для дальнейшего использования.

У меня есть код, который выглядит примерно так:

@strings = ('abc.abc#a', 'def.g.h#i');
foreach my $str (@strings){
    $str =~ s/[^0-9A-Za-z]/_/g;
    print $str, "\n"; #Actually I use the string to manipulate files.
}

Я мог бы решить проблему, выполнив следующее:

@strings = ('abc.abc#a', 'def.g.h#i');
foreach my $str (@strings){
    my $temp = $str; #copy to a temporary value
    $temp =~ s/[^0-9A-Za-z]/_/g;
    print $temp, "\n"; #$str remains untouched...
}

но есть ли более эффективный способ сделать это?

Большое спасибо!

Ответы [ 4 ]

16 голосов
/ 10 июля 2010

Ты не сумасшедший;это нормальное поведение.См. perldoc perlsyn в циклах Foreach:

Если какой-либо элемент LIST является lvalue, вы можете изменить его, изменив VAR внутри цикла.И наоборот, если какой-либо элемент LIST НЕ является lvalue, любая попытка изменить этот элемент потерпит неудачу.Другими словами, индексная переменная цикла «foreach» является неявным псевдонимом для каждого элемента в списке, над которым вы циклически повторяетесь.похожее поведение:

map BLOCK LIST  
map EXPR,LIST

...
Обратите внимание, что $ _ является псевдонимом значения списка, поэтому его можно использовать для изменения элементов LIST.Хотя это полезно и поддерживается, оно может привести к странным результатам, если элементы LIST не являются переменными.Использование регулярного цикла «foreach» для этой цели было бы более понятным в большинстве случаев.См. Также «grep» для массива, состоящего из тех элементов исходного списка, для которых BLOCK или EXPR оценивается как true.

Вы можете переписать свой код таким образом, что по крайней мере спасет васдобавление дополнительной строки:

my @strings = ('abc.abc#a', 'def.g.h#i');
foreach my $str (@strings){
    (my $copy = $str) =~ s/[^0-9A-Za-z]/_/g;
    print $copy, "\n";
}
3 голосов
/ 10 июля 2010

Вы всегда можете сделать копию массива перед изменением элементов в нем ('my' добавлено, чтобы успокоить strict), а именно:1007 *

@temp не имеет области видимости за пределами цикла foreach.

2 голосов
/ 10 июля 2010

Вы правы.Как указывает Programming Perl , переменная цикла является псевдонимом для текущего элемента массива, и вы должны сохранить переменную цикла, если какие-либо изменения не влияют на исходное значение.

1 голос
/ 23 октября 2013
my @strings = ('abc.abc#a', 'def.g.h#i');

print join( "\n", ( map { $_ =~ s/[^0-9A-Za-z]/_/gr } @strings ) )."\n";

r работает только в Perl 5.14 и выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...