При попытке сделать это:
my $obj = new JavaScript::Minifier;
$obj->minify(*STDIN, *STDOUT);
// modified above line to
$obj->minify(*IP_HANDLE,*OP_HANDLE)
Вышеупомянутые работы, если IP_HANDLE и OP_HANDLE являются дескрипторами файлов, но тем не менее я не в состоянии выяснить что на самом деле *
делает при применении к дескриптору файла или любому другому типу данных.
Спасибо,
В старые добрые времена до выхода perl v5.6, в которой были введены лексические дескрипторы файлов - более десяти лет назад - передача дескрипторов файлов и каталогов была неудобной. Код из вашего вопроса написан с использованием этого старомодного стиля.
Техническое название *STDIN
, например, typeglob, объясняется в разделе "Typeglobs and Filehandles" раздела perldata. Вы можете встретить манипуляции с typeglobs для различных целей в унаследованном коде. Обратите внимание, что вы можете захватывать typeglobs только глобальных переменных, но никак не лексических.
Передача дескрипторов была распространенной целью для работы непосредственно с typeglobs, но были и другие применения. Подробности см. ниже.
*foo{THING}
синтаксисВ документации perldata объясняется:
Typeglobs и Filehandles
Perl использует внутренний тип, называемый typeglob, для хранения всей записи таблицы символов. Префиксом типа typeglob является
*
, поскольку он представляет все типы. Раньше это был предпочтительный способ передачи массивов и хэшей по ссылке в функцию, но теперь, когда у нас есть реальные ссылки, это редко требуется.[...]
Еще один способ использования typeglobs - передача файловых хэндлов в функцию или создание новых файловых хэндлов. Если вам нужно использовать typeglob для сохранения файла, сделайте это так:
$fh = *STDOUT;
или, возможно, как настоящую ссылку, вот так:
$fh = \*STDOUT;
Примеры использования их в качестве косвенных ссылок на файлы в функциях см. в perlsub.
Ниже приведен ссылочный раздел perlsub.
Передача записей таблицы символов (typeglobs)
ВНИМАНИЕ: Механизм, описанный в этом разделе, изначально был единственным способом имитации передачи по ссылке в старых версиях Perl. Хотя он по-прежнему хорошо работает в современных версиях, новый механизм ссылок в целом проще в работе. См. ниже.
Иногда вы хотите передать в подпрограмму не значение массива, а его имя, чтобы подпрограмма могла изменять глобальную копию массива, а не работать с локальной копией. В Perl вы можете ссылаться на все объекты с определенным именем, добавляя к имени звездочку:
*foo
. Это часто называют "typeglob", потому что звезду спереди можно рассматривать как подстановочный знак для всех забавных символов префикса переменных, подпрограмм и т.п..При оценке 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');
или, может быть, вызов sub
$ 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.
Один трюк с typeglobs, удобный для постов Stack Overflow -
*ARGV = *DATA;
(я мог бы быть более точным с *ARGV = *DATA{IO}
, но это немного суетливо. )
Это позволяет оператору diamond <>
читать из хэндла файла DATA
, как в
#! /usr/bin/perl
*ARGV = *DATA; # for demo only; remove in production
while (<>) { print }
__DATA__
Hello
there
Таким образом, программа и ее входные данные могут находиться в одном файле, а код более близок к тому, как он будет выглядеть на производстве: просто удалите присвоение typeglob.
Временные значения через
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.
"When to Still Use local()
" в perlsub имеет другой пример.
2. Вам нужно создать локальный файл или директорию или локальную функцию.
Функция, которой нужен собственный хэндл файла, должна использовать
local()
на полном typeglob. Это можно использовать для создания новых записей в таблице символов:sub ioqueue { local (*READER, *WRITER); # не мое! pipe (READER, WRITER) или die "pipe: $!"; return (*READER, *WRITER); } ($head, $tail) = ioqueue();
Чтобы подчеркнуть, этот стиль старомоден. Предпочтительнее избегать глобальных filehandles в новом коде, но возможность понять эту технику в существующем коде полезна.
*foo{THING}
синтаксисВы можете получить доступ к различным частям typeglob, как объясняет 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
, если этот конкретный THING еще не использовался, за исключением скаляров.*foo{SCALAR}
возвращает ссылку на анонимный скаляр, если$foo
еще не использовался. Это может измениться в будущем выпуске.
*foo{IO}
является альтернативой механизму*HANDLE
, приведенному в ["Typeglobs and Filehandles" в perldata] для передачи файловых обращений в или из подпрограмм, или хранения в больших структурах данных. Его недостаток в том, что он не создаст для вас новый файл-хэндл. Его преимущество заключается в том, что при присвоении typeglob у вас меньше риска получить больше, чем вы хотите. Однако, если вы присваиваете входящее значение скаляру, а не typeglob, как в примерах ниже, то такой риск отсутствует.splutter(*STDOUT); # передаем весь glob целиком splutter(*STDOUT{IO}); # передаем хэндлы файла и dir sub splutter { my $fh = shift; print $fh "her um well a hmmm\n"; } $rec = get_rec(*STDIN); # передаем весь glob целиком $rec = get_rec(*STDIN{IO}); # передаем хэндлы файлов и директорий sub get_rec { my $fh = shift; return scalar <$fh>; }
Контекст - ключевой момент в Perl. В вашем примере, хотя синтаксис может быть двусмысленным, смысл не является таковым: даже если параметры являются строками, эти строки явно предназначены для именования ручек файлов.
Поэтому рассмотрим все случаи, которые minify
может потребоваться обработать:
Например:
#! /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!
Как автору библиотеки, быть аккомодативным может быть полезно. Для примера, следующая заглушка JavaScript::Minifier понимает как старомодные, так и современные способы передачи filehandle.
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;
Output:
$ ./prog.pl Name "main::OP_HANDLE" used only once: possible typo at ./prog.pl line 7. Ahoy there matey! Ahoy there matey!
*
относится к Perl «typeglob» , который является неясной деталью реализации Perl. Некоторый старый код Perl должен ссылаться на дескрипторы файлов с помощью typeglobs (поскольку в то время не было другого способа сделать это). Более современный код может вместо этого использовать ссылки на дескрипторы файлов, с которыми легче работать.
*
аналогичен $
или %
, он относится к другому типу объекта, известному под тем же именем.
Со страницы документации perldata
:
Perl использует внутренний тип, называемый typeglob, для хранения всей записи таблицы символов. Префиксом типа typeglob является *, потому что он представляет все типы. Раньше это был предпочтительный способ передачи массивов и хэшей по ссылке в функцию, но теперь, когда у нас есть реальные ссылки, это редко требуется.
Эта команда выполнит то, что вы хотите.
(defun annotate-todo ()
"put fringe marker on TODO: lines in the curent buffer"
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "TODO:" nil t)
(let ((overlay (make-overlay (- (point) 5) (point))))
(overlay-put overlay 'before-string (propertize "A"
'display '(left-fringe right-triangle)))))))
При необходимости можно настроить растровое изображение .
Чтобы это можно было применить ко всем файлам, можно добавить его в 'find-file-hooks
(add-hook 'find-file-hooks 'annotate-todo)
Или, если вы хотите, чтобы это было только для определенных режимов, вы можете добавить его к этим режимам.
См. Цветовые поля , Свойство « дисплей» , Наложение и, самое главное, свойство перед строкой .
Примечание. Код был обновлен 27/02/2010 для использования наложений вместо непосредственного добавления свойств текста к текущему тексту.
-121--2212752-Или маленький язык домена:
a = [1, 2, 3, 4]
FirstMiddleLast.iterate(a) do
first do |e|
p [e, 'first']
end
middle do |e|
p [e, 'middle']
end
last do |e|
p [e, 'last']
end
end
# => [1, "first"]
# => [2, "middle"]
# => [3, "middle"]
# => [4, "last"]
и код, который заставляет его пойти:
class FirstMiddleLast
def self.iterate(array, &block)
fml = FirstMiddleLast.new(array)
fml.instance_eval(&block)
fml.iterate
end
attr_reader :first, :middle, :last
def initialize(array)
@array = array
end
def first(&block)
@first = block
end
def middle(&block)
@middle = block
end
def last(&block)
@last = block
end
def iterate
@first.call(@array.first) unless @array.empty?
if @array.size > 1
@array[1..-2].each do |e|
@middle.call(e)
end
@last.call(@array.last)
end
end
end
Я начал думать: «Если бы только вы могли передать несколько блоков функции Ruby, то вы могли бы иметь ловкое и простое решение этого вопроса». Тогда я понял, что DSL играют маленькие трюки, которые почти как прохождение нескольких блоков.
-121--1000513- Это глобсигил. * FOO
относится к глобусу с именем «FOO», так же как $ FOO
относится к скаляру с именем «FOO» и так далее. Как правило, глобусы являются кодовыми ссылками или дескрипторами файлов.
Для изменения значения glob, например * name _ of _ sub = sub {};
, или для получения его значения без вызова специального синтаксиса, например, вызова sub.