Почему программирование Perl использует локальные (не мои) для файловых дескрипторов? - PullRequest
11 голосов
/ 05 марта 2009

Когда я читаю Программирование на Perl , 2-е издание, стр. 51, что-то меня смущает:

sub newopen {
    my $path = shift;
    local *FH;    #not my!
    open (FH, $path) || return undef;
    return *FH;
}

$fh = newopen('/etc/passwd');

Мой Я знаю, почему мы не рекомендуем использовать мой? Пока я не вижу, что что-то пойдет не так, если мы будем использовать my ().

Спасибо!

Ответы [ 5 ]

25 голосов
/ 05 марта 2009

Банально, что вы должны использовать local, потому что my *FH - синтаксическая ошибка.

«Правильный» (но не очень поучительный) ответ заключается в том, что вы делаете это неправильно. Вместо этого вы должны использовать лексические файловые дескрипторы и форму с тремя аргументами open.

sub newopen {
    my $path = shift;
    my $fh;
    open($fh, '<', $path) or do {
        warn "Can't read file '$path' [$!]\n";
        return;
    }
    return $fh;
}

Чтобы действительно ответить , почему требуется объяснить разницу между лексическими и глобальными переменными, а также между областью действия переменной и ее продолжительностью.

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

my объявляет лексическую переменную. Лексические переменные имеют область видимости от точки объявления до конца включающего блока (или файла). Вы можете иметь другие переменные с одинаковыми именами в разных областях без конфликта. (Вы также можете повторно использовать имя в перекрывающихся областях, но не делайте этого.) Длительность лексических переменных управляется подсчетом ссылок. Пока существует хотя бы одна ссылка на переменную, значение существует, даже если имя недопустимо в определенной области видимости! my также имеет эффект времени выполнения - он выделяет новую переменную с заданным именем.

local немного отличается. Он работает с глобальными переменными. Глобальные переменные имеют глобальную область действия (имя действует везде) и продолжительность всей жизни программы. local временно изменяет значение глобальной переменной. Это иногда называют «динамическим определением». Изменение начинается в точке объявления local и сохраняется до конца включающего блока, после которого восстанавливается старое значение. Важно отметить, что новое значение не ограничивается блоком - оно видно везде (включая вызываемые подпрограммы). Правила подсчета ссылок по-прежнему применяются, поэтому вы можете взять и сохранить ссылку на локализованное значение после истечения срока изменения.

Вернуться к примеру: *FH - глобальная переменная. Точнее, это «typeglob» - контейнер для набора глобальных переменных. Тип-глоб содержит слот для каждого из основных типов переменных (скаляр, массив, хэш), а также несколько других вещей. Исторически сложилось, что Perl использовал typeglobs для хранения файловых дескрипторов, и local -изменение их помогло гарантировать, что они не забивают друг друга. У лексических переменных нет typeglobs, поэтому высказывание my *FH является синтаксической ошибкой.

В современных версиях Perl лексические переменные могут и должны использоваться вместо файловых дескрипторов. И это возвращает нас к «правильному» ответу.

20 голосов
/ 05 марта 2009

В вашем примере кода при вызове встроенной подпрограммы open в качестве дескриптора файла используется пустое слово, которое является эквивалентом глобальной переменной. Как объяснил ответ Натана Феллмана , использование local локализует это голое слово в текущем блоке кода, если другая глобальная переменная с таким же именем определена в другом месте в скрипте или модуле. Это предотвратит уничтожение ранее определенной глобальной переменной новой декларацией.

Это было очень распространенной практикой в ​​старые времена Perl, но с Perl 5.6 гораздо лучше использовать скаляр (с объявлением my, на которое вы намекнули в своем вопросе), чтобы определить свой файл обрабатывать и, кроме того, использовать вызов трех аргументов для open.

use Carp;
open my $error_log, '>>', 'error.log' or croak "Can't open error.log: $OS_ERROR";

Кроме того, обратите внимание, что для стандартного чтения и записи ввода / вывода все же лучше использовать два аргумента open:

use Carp;
open my $stdin, '<-' or croak "Can't open stdin: $OS_ERROR";

Кроме того, вы можете использовать модуль IO::File, чтобы благословить дескриптор файла для класса:

use IO::File;
my $error_log = IO::File->new('error.log', '>>') or croak "Can't open error.log: $OS_ERROR");

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

12 голосов
/ 05 марта 2009

Почему ты читаешь устаревшую книгу. Третье издание уже давно вышло! Какую версию Perl вы используете? Второе издание описывает Perl 5.004 (5.4.x) или около того.

В наши дни не следует использовать нотацию typeglob для файловых дескрипторов; используйте вместо этого «лексические дескрипторы файлов» (см. open , я думаю) или модуль FileHandle или один из его родственников.


Спасибо Michael Schwern и Ysth за комментарии, включенные здесь.

0 голосов
/ 05 марта 2009

См. Локализованные файловые дескрипторы здесь , наверное, это объясняет.

0 голосов
/ 05 марта 2009

Я полагаю, это потому, что my выделяет новую копию переменной в стеке, и она теряется при выходе из блока. local сохраняет существующий *FH в другом месте и переопределяет существующий *FH. Восстанавливает старый при выходе из стека. С my тип * глобус *FH выходит из области видимости при выходе из блока. С local он продолжает существовать, так что вы можете продолжать использовать его после возврата.

Я не уверен на 100% в этом, но, возможно, это может указать вам правильное направление.

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