Почему я должен использовать * перед дескриптором файла Perl bareword? - PullRequest
9 голосов
/ 11 февраля 2010

При попытке сделать это:

 my $obj = new JavaScript::Minifier;
 $obj->minify(*STDIN, *STDOUT);
// modified above line to
 $obj->minify(*IP_HANDLE,*OP_HANDLE)

Вышеуказанное работает, если IP_HANDLE и OP_HANDLE являются файловыми дескрипторами, но я все еще не могу понять, что на самом деле * делает при применении к файловому дескриптору или любому другому типу данных.

Спасибо

Ответы [ 3 ]

25 голосов
/ 11 февраля 2010

В старые добрые времена, предшествующие Perl v5.6, который ввел лексические файловые дескрипторы - более чем десять лет назад - передача дескрипторов файлов и каталогов была неудобной. Код из вашего вопроса написан с использованием этого старомодного стиля.

Техническое имя для *STDIN, например, это typeglob , объясненное в разделе «Typeglobs and Filehandles» perldata . Вы можете столкнуться с манипуляциями с typeglobs для различных целей в унаследованном коде. Обратите внимание, что вы можете захватывать только типы глобальных переменных, но не лексические.

Передача дескрипторов была обычным делом для непосредственного обращения с типоглобами, но были и другие применения. Подробнее см. Ниже.

  • Передача файловых дескрипторов подпрограммам
  • Синтаксическая неоднозначность: строка или дескриптор файла
  • Псевдонимы с помощью назначения typeglob
  • Локализация ручек с помощью локализации typeglobs
  • заглядывать под капот: *foo{THING} синтаксис
  • Свяжите все это вместе: DWIM!

Передача файловых дескрипторов подпрограммам

В документации perldata объясняется :

Typeglobs и Filehandles

Perl использует внутренний тип, называемый typeglob , для хранения всей записи таблицы символов. Префикс типа для typeglob - *, потому что он представляет все типы. Раньше это был предпочтительный способ передачи массивов и хэшей по ссылке в функцию, но теперь, когда у нас есть реальные ссылки, это редко требуется.

[...]

Другое использование для typeglobs - передача файловых дескрипторов в функцию или создание новых файловых дескрипторов. Если вам нужно использовать typeglob для сохранения дескриптора файла, сделайте это следующим образом:

$fh = *STDOUT;

или, возможно, в качестве реальной ссылки, например:

$fh = \*STDOUT;

См. perlsub для примеров использования их в качестве косвенных файловых дескрипторов в функциях.

Секция , на которую ссылается perlsub , находится ниже.

Проходные записи в таблице символов (typeglobs)

ПРЕДУПРЕЖДЕНИЕ: Механизм, описанный в этом разделе, первоначально был единственным способом имитации передачи по ссылке в более старых версиях Perl. Хотя в современных версиях он по-прежнему работает нормально, с новым справочным механизмом, как правило, легче работать. Смотри ниже.

Иногда вам не нужно передавать значение массива в подпрограмму, а скорее его имя, чтобы подпрограмма могла изменять глобальную копию, а не работать с локальной копией. В Perl вы можете ссылаться на все объекты с определенным именем, поставив перед именем звездочку: *foo. Это часто называют «typeglob», потому что звезду на передней панели можно рассматривать как подстановочный знак для всех смешных префиксных символов в переменных и подпрограммах и тому подобном.

При оценке typeglob создает скалярное значение, которое представляет все объекты с таким именем, включая любой дескриптор файла, формат или подпрограмму. При назначении, это приводит к тому, что упомянутое имя ссылается на любое значение *, присвоенное ему. [...]

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

Синтаксическая неоднозначность: строка или дескриптор файла?

Без символа * голое слово - это просто строка.

Простых струн иногда достаточно, но. Например, оператор print позволяет

$ perl -le 'print { "STDOUT" } "Hiya!"'
Hiya!

$ perl -le '$h="STDOUT"; print $h "Hiya!"'
Hiya!

$ perl -le 'print "STDOUT" +123'
123

Сбой при включенном strict 'refs'. Руководство объясняет:

FILEHANDLE может быть именем скалярной переменной, в этом случае переменная содержит имя или ссылку на дескриптор файла, таким образом вводя один уровень косвенности.

В вашем примере рассмотрим синтаксическую неоднозначность. Без символа * вы могли бы иметь в виду строки

$ perl -MO=Deparse,-p prog.pl
use JavaScript::Minifier;
(my $obj = 'JavaScript::Minifier'->new);
$obj->minify('IP_HANDLE', 'OP_HANDLE');

или, возможно, дополнительный вызов

$ perl -MO=Deparse,-p prog.pl
use JavaScript::Minifier;
sub OP_HANDLE {
    1;
}
(my $obj = 'JavaScript::Minifier'->new);
$obj->minify('IP_HANDLE', OP_HANDLE());

или снаш, файловый дескриптор. Обратите внимание, что в приведенных выше примерах голое слово JavaScript::Minifier также компилируется как простая строка.

Включите прагму strict, и все равно это все исчезнет:

$ perl -Mstrict prog.pl
Bareword "IP_HANDLE" not allowed while "strict subs" in use at prog.pl line 6.
Bareword "OP_HANDLE" not allowed while "strict subs" in use at prog.pl line 6.

Псевдонимы с помощью назначения typeglob

Один трюк с typeglobs, который удобен для сообщений переполнения стека, это

*ARGV = *DATA;

(Я мог бы быть более точным с *ARGV = *DATA{IO}, но это немного суетливо.)

Это позволяет оператору алмаза <> читать из файлового дескриптора DATA, как в

#! /usr/bin/perl

*ARGV = *DATA;   # for demo only; remove in production

while (<>) { print }

__DATA__
Hello
there

Таким образом, программа и ее входные данные могут быть в одном файле, и код более близок к тому, как он будет выглядеть в рабочей среде: просто удалите присвоение typeglob.

Локализация ручек с помощью локализации typeglobs

Как отмечено в perlsub

Временные значения через local()

ПРЕДУПРЕЖДЕНИЕ: Как правило, вы должны использовать my вместо local, потому что это быстрее и безопаснее. Исключениями являются глобальные переменные пунктуации, глобальные файловые дескрипторы и форматы, а также прямое манипулирование самой таблицей символов Perl. local чаще всего используется, когда текущее значение переменной должно быть видимым для вызываемых подпрограмм. [...]

вы можете использовать typeglobs для локализации файловых дескрипторов:

$ cat prog.pl
#! /usr/bin/perl

sub foo {
  local(*STDOUT);
  open STDOUT, ">", "/dev/null" or die "$0: open: $!";
  print "You can't see me!\n";
}

print "Hello\n";
foo;
print "Good bye.\n";

$ ./prog.pl
Hello
Good bye.

«Когда все еще использовать local()» в perlsub имеет другой пример.

2. Вам необходимо создать локальный дескриптор файла или каталога или локальную функцию.

Функция, которой нужен собственный файловый дескриптор, должна использовать local() для полного typeglob. Это может быть использовано для создания новых записей таблицы символов:

sub ioqueue {
    local (*READER, *WRITER); # not my!
    pipe (READER, WRITER) or die "pipe: $!";
    return (*READER, *WRITER);
}
($head, $tail) = ioqueue();

Чтобы подчеркнуть, этот стиль старомоден. Предпочитаю избегать глобальных файловых дескрипторов в новом коде, но полезно понимать технику в существующем коде.

Подглядывание под капотом: *foo{THING} Синтаксис

Вы можете получить в разных частях типа глоб, как perlref объясняет:

Ссылка может быть создана с использованием специального синтаксиса, который с любовью называют синтаксисом *foo{THING}. *foo{THING} возвращает ссылку на слот THING в *foo (это запись таблицы символов, которая содержит все, что называется foo).

$scalarref = *foo{SCALAR};
$arrayref = *ARGV{ARRAY};
$hashref = *ENV{HASH};
$coderef = *handler{CODE};
$ioref = *STDIN{IO};
$globref = *foo{GLOB};
$formatref = *foo{FORMAT};

Все они говорят сами за себя, кроме *foo{IO}. Он возвращает дескриптор ввода-вывода, используемый для дескрипторов файлов (open), сокетов (socket и socketpair) и дескрипторов каталогов (opendir). Для совместимости с предыдущими версиями Perl *foo{FILEHANDLE} является синонимом *foo{IO}, хотя с 5.8.0 устарел. Если предупреждения об устаревании действуют, он предупредит об их использовании.

*foo{THING} возвращает undef, если эта конкретная вещь еще не использовалась, за исключением скаляров. *foo{SCALAR} возвращает ссылку на анонимный скаляр, если $foo еще не использовался. Это может измениться в будущем выпуске.

*foo{IO} является альтернативой механизму *HANDLE, приведенному в [«Typeglobs and Filehandles» в perldata] для передачи файловых дескрипторов в или из подпрограмм или для хранения в более крупных структурах данных. Его недостаток в том, что он не будет создавать новый дескриптор файла для вас. Его преимущество в том, что у вас меньше риск забить больше, чем вы хотите с помощью назначения типа «глоб». (Тем не менее, он по-прежнему сопоставляет дескрипторы файлов и каталогов.) Однако, если вы присваиваете входящее значение скаляру вместо глобуса типа, как мы это делаем в примерах ниже, риска не будет.

splutter(*STDOUT); # pass the whole glob
splutter(*STDOUT{IO}); # pass both file and dir handles

sub splutter {
  my $fh = shift;
  print $fh "her um well a hmmm\n";
}

$rec = get_rec(*STDIN); # pass the whole glob
$rec = get_rec(*STDIN{IO}); # pass both file and dir handles

sub get_rec {
  my $fh = shift;
  return scalar <$fh>;
}

Свяжите все это вместе: DWIM!

Контекст является ключевым с Perl. В вашем примере, хотя синтаксис может быть неоднозначным, это не так: даже если параметры являются строками, эти строки явно предназначены для именования файловых дескрипторов.

Итак, рассмотрим все случаи minify, возможно, потребуется обработать:

  • bareword
  • голый шрифт
  • ссылка на typeglob
  • файловая ручка в скаляре

Например:

#! /usr/bin/perl

use warnings;
use strict;

*IP_HANDLE = *DATA;
open OP_HANDLE, ">&STDOUT";
open my $fh, ">&STDOUT";
my $offset = tell DATA;

use JavaScript::Minifier;
my $obj = JavaScript::Minifier->new;
$obj->minify(*IP_HANDLE, "OP_HANDLE");

seek DATA, $offset, 0 or die "$0: seek: $!";
$obj->minify(\*IP_HANDLE, $fh);

__DATA__
Ahoy there
matey!

как библиотека autHor, будучи любезным, может быть полезным. Чтобы проиллюстрировать это, следующий фрагмент JavaScript :: Minifier понимает как старомодные, так и современные способы передачи файловых дескрипторов.

package JavaScript::Minifier;

use warnings;
use strict;

sub new { bless {} => shift }

sub minify {
  my($self,$in,$out) = @_;

  for ($in, $out) {
    no strict 'refs';
    next if ref($_) || ref(\$_) eq "GLOB";

    my $pkg = caller;
    $_ = *{ $pkg . "::" . $_ }{IO};
  }

  while (<$in>) { print $out $_ }
}

1;

Выход:

$ ./prog.pl
Name "main::OP_HANDLE" used only once: possible typo at ./prog.pl line 7.
Ahoy there
matey!
Ahoy there
matey!
7 голосов
/ 11 февраля 2010

* относится к Perl "typeglob" , который является неясной деталью реализации Perl. Некоторый старый код Perl должен ссылаться на дескрипторы файлов с помощью typeglobs (поскольку в то время не было другого способа сделать это). Более современный код может вместо этого использовать ссылки на файловые дескрипторы, с которыми легче работать.

* аналогично $ или %, оно относится к объекту другого типа, известному под тем же именем.

Со страницы документации perldata:

Perl использует внутренний тип, называемый typeglob, для хранения всей записи таблицы символов. Префикс типа для typeglob - *, потому что он представляет все типы. Раньше это был предпочтительный способ передачи массивов и хэшей по ссылке в функцию, но теперь, когда у нас есть реальные ссылки, это редко требуется.

2 голосов
/ 11 февраля 2010

Это сигара. *FOO относится к глобу с именем "FOO", точно так же, как $FOO относится к скаляру с именем "FOO" и так далее. Глобусы - это обычно ссылки на код или файловые дескрипторы.

Вам необходим присутствующий символ, чтобы изменить значение glob, например, *name_of_sub = sub{};, или получить его значение без вызова специального синтаксиса, например, вызова sub.

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