Все задачи

Преданья старины глубокой

15 Oct 2013

Программист оказался вовлеченным в старый проект с большим количеством кода на Perl. В числе прочего программист нашел такой код для выделения первого поля из стандартного входного потока (что-то вроде awk '{print $1}'):

while(<>) {
    chomp;
    split;
    print $_[0]."\n";
}

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

Почему работал этот код? Что с ним случилось потом?

Подсказка

Показать

Код не менялся, данные тоже. Менялся perl.

Подсказка-2

Показать

perl 5.10.1: split

perl 5.12.0: split

Разоблачение

Показать

Вплоть до версии 5.10 split в скалярном контексте не только возвращал количество полей, на которые он разбил строку, но и записывал полученные поля в массив @_. Впрочем, пользоваться этим поведеним документация не рекомендовала:

In scalar context, returns the number of fields found. In scalar and void context it splits into the @_ array. Use of split in scalar and void context is deprecated, however, because it clobbers your subroutine arguments.

В скалярном контексте возвращает количество найденных полей. В скалярном и пустом контексте записывает результат в массив @_ Однако, использование split в скалярном и пустом контексте строго не рекомендуется, потому что оно портит аргументы функций

Именно благодаря этому поведению код из задачи работал на старых версиях perl.

Начиная с версии 5.12 split не записывает свой результат в @_, присваивание надо выполнять самостоятельно:

my @a = split;
print $a[0];

Самое полное и точное объяснение проблемы прислал наш читатель Тигран Оганесян. Класс!