Почему Perl file glob() не работает вне цикла в скалярном контексте?
Согласно документации Perl о глобализации файлов, оператор <*> или функция glob() при использовании в скалярном контексте должны перебирать список файлов, соответствующих указанному шаблону, возвращая следующее имя файла каждый раз, когда он вызывается или undef, когда больше нет файлов.
Но, похоже, что итерационный процесс работает только внутри цикла. Если он не находится в цикле, то кажется, что он начинается сначала, прежде чем все значения будут прочитаны.
Из документов Perl:
В скалярном контексте glob повторяет такие расширения имени файла, возвращая undef, когда список исчерпан.
http://perldoc.perl.org/functions/glob.html
Однако в скалярном контексте оператор возвращает следующее значение каждый раз, когда оно вызывается, или undef, когда список заканчивается.
http://perldoc.perl.org/perlop.html#I/O-Operators
Пример кода:
использовать предупреждения; использовать строгое; мой $filename; # в скалярном контексте <*> должен возвращать следующее имя файла # каждый раз, когда он вызывается, или undef, когда в списке заканчивается $ filename = <*>; напечатать "$filename\n"; $ filename = <*>; # не работает как задокументировано, начинается заново и печатает "$filename\n"; # всегда возвращает одно и то же имя файла $ filename = <*>; напечатать "$filename\n"; напечатайте "\n"; выведите "$ filename \ n", а $ filename = <*>; # работает в цикле, возвращает следующий файл # каждый раз, когда он вызывается
В каталоге с 3 файлами...file1.txt, file2.txt и file3.txt приведенный выше код выведет:
file1.txt file1.txt file1.txt file1.txt file2.txt file3.txt
Примечание. Фактический сценарий perl должен находиться за пределами тестового каталога, иначе вы увидите имя файла сценария в выходных данных.
Я что-то здесь не так делаю или это так должно работать?
4 ответа
Вот способ захватить магию <>
превращать состояние оператора в объект, которым вы можете манипулировать обычным способом: анонимные подпрограммы (и / или замыкания)!
sub all_files {
return sub { scalar <*> };
}
my $iter = all_files();
print $iter->(), "\n";
print $iter->(), "\n";
print $iter->(), "\n";
или возможно:
sub dir_iterator {
my $dir = shift;
return sub { scalar glob("$dir/*") };
}
my $iter = dir_iterator("/etc");
print $iter->(), "\n";
print $iter->(), "\n";
print $iter->(), "\n";
Опять же, я склонен подать это под "любопытство". Не обращайте внимания на эту странность glob()
/ <>
и использовать opendir
/readdir
, IO:: Все/readdir
или File:: Glob вместо:)
Следующий код также, кажется, создает 2 отдельных экземпляра итератора...
для (1..3) { $ filename = <*>; выведите "$filename\n", если определено $ filename; $ filename = <*>; выведите "$filename\n", если определено $filename; }
Я думаю, что я вижу логику там, но это отчасти противоречит интуиции и противоречит документации. В документах ничего не говорится о необходимости быть в цикле, чтобы итерация работала.
Также из perlop
:
Глобус (файл) оценивает свой (встроенный) аргумент только тогда, когда он начинает новый список.
призвание glob
создает список, который либо возвращается целиком (в контексте списка), либо извлекается по одному элементу за раз (в скалярном контексте). Но каждый звонок glob
создает отдельный список.
(Выцарапывая мою ржавую память о Perl...) Я думаю, что несколько лексических примеров <*>
обрабатываются как независимые вызовы glob, тогда как в цикле while вы вызываете один и тот же "экземпляр" (что бы это ни значило).
Представьте себе, например, если вы сделали это:
while (<*>) { ... }
...
while (<*>) { ... }
Вы, конечно, не ожидаете, что эти два вызова будут мешать друг другу.