Подтвердить что ты не робот

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

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

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

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

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

Спасибо!

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Это была очень распространенная практика в старые дни 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, вы обязаны сделать это для себя, чтобы купить эту книгу.

Ответ 3

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

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


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

Ответ 4

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

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

Ответ 5

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