В Perl цикл foreach позволяет удобно перебирать элементы массива, причём переменная цикла по умолчанию ($_, либо явно указанная) ссылается на элемент массива, а не копирует его значение.
Пример:
my @arr = (1, 2, 3); foreach my $x (@arr) { $x *= 2; } # @arr равен (2, 4, 6) — элементы изменены!
Чтобы получить именно копию значения (а не ссылку на оригинальный элемент), нужно явно присвоить переменную:
foreach my $x ( @arr ) { # $x — ссылка на элемент ... } # ↓↓↓↓ foreach (@arr) { my $copy = $_; ... } # $copy — копия, $_ — ссылка на элемент
Не следует использовать одну и ту же переменную в нескольких вложенных циклах foreach, иначе можно получить неожиданные побочные эффекты.
Является ли переменная в foreach-цикле независимой копией, и можно ли её безопасно изменять внутри тела цикла?
Обычно отвечают: "Да, ведь цикл сам создаёт переменную — можно менять как угодно."
Правильный ответ:
В Perl переменная цикла по умолчанию (например, foreach $var (@arr)) является алиасом (ссылкой) на элемент массива. Любое изменение переменной цикла ведёт к изменению оригинального элемента!
Чтобы изменять временную копию — делайте явное присваивание внутри цикла:
foreach my $elem (@arr) { my $t = $elem; $t++; # только копия, оригинальный массив не меняется }
История
История 1
В задаче по фильтрации записей программист модифицировал переменную своего цикла, не подозревая, что меняет оригинальные данные. Последующие вычисления шли по изменённому массиву, из-за чего сумма элементов считалась неверно. Ошибка проявилась только при сравнении с референтной выборкой.
История 2
Вложенный foreach использовал одну и ту же переменную ($item). Переменная внутреннего цикла затёрла состояние внешней, из-за чего были потеряны результаты первого прохода, а часть массива оказалась с неверными значениями.
История 3
Массив ссылок на хэши перебирался через foreach, и в каждом цикле переменной цикла присваивалось новое значение. В результате ссылки были "сломаны" — вместо ссылочных значений теперь стояли скаляры, и следующий этап обработки упал с ошибкой типа.