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

StackOverflow https://stackoverflow.com/questions/613906

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Спасибо!

Это было полезно?

Решение

Банальный ответ заключается в том, что вы должны использовать 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" - контейнер для набора глобальных переменных.typeglob содержит слот для каждого из основных типов переменных (scalar, array, hash) плюс несколько других параметров.Исторически сложилось так, что Perl использовал typeglobs для хранения дескрипторов файлов и local- знакомство с ними помогло убедиться, что они не ударили друг друга.Лексические переменные не имеют typeglobs, поэтому говорят my *FH это синтаксическая ошибка.

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

Другие советы

В вашем примере кода вызов встроенной подпрограммы 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";

В качестве отступления, пожалуйста, обратите внимание, что для стандартного чтения и записи ввода / вывода все же лучше использовать аргумент two 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).Если вы серьезно относитесь к разработке на Perl, вы обязаны ради себя приобрести эту книгу.

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

В наши дни вам не следует использовать обозначение typeglob для дескрипторов файлов;используйте "лексические дескрипторы файлов" (см. Открыть, я думаю) или Дескриптор файла модуль или вместо него один из его родственников.


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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top