Почему 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 (<*>) { ... }

Вы, конечно, не ожидаете, что эти два вызова будут мешать друг другу.

Другие вопросы по тегам