Подтвердить что ты не робот

Почему Perl файл glob() работает вне цикла в скалярном контексте?

В соответствии с документацией Perl по файловому расширению, < * > оператор или glob(), при использовании в скалярном контексте, должен перебирать список файлов, соответствующих указанному шаблону, возвращая следующее имя файла при каждом вызове или undef, когда файлов больше нет.

Но процесс итерации, похоже, работает только внутри цикла. Если он не находится в цикле, то он, кажется, начинается сразу перед тем, как все значения будут прочитаны.

Из документов Perl:

В скалярном контексте glob выполняет итерацию через такие расширения имен файлов, возвращая undef, когда список исчерпан.

http://perldoc.perl.org/functions/glob.html

Однако в скалярном контексте оператор возвращает каждое значение при каждом вызове или undef, когда список заканчивается.

http://perldoc.perl.org/perlop.html#I/O-Operators

Пример кода:

use warnings;
use strict;

my $filename;

# in scalar context, <*> should return the next file name
# each time it is called or undef when the list has run out

$filename = <*>;
print "$filename\n";
$filename = <*>;      # doesn't work as documented, starts over and
print "$filename\n";  # always returns the same file name
$filename = <*>;
print "$filename\n";

print "\n";

print "$filename\n" while $filename = <*>; # works in a loop, returns next file
                                           # each time it is called

В каталоге с 3 файлами... file1.txt, file2.txt и file3.txt выведенный выше код:

file1.txt
file1.txt
file1.txt

file1.txt
file2.txt
file3.txt

Примечание. Фактический perl script должен находиться за пределами тестового каталога, или вы увидите имя файла script на выходе.

Я делаю что-то неправильно здесь, или это то, как он должен работать?

4b9b3361

Ответ 1

Здесь показан способ захвата магии состояния оператора glob-оператора <> в объект, который вы можете манипулировать обычным способом: анонимные субмарины (и/или замыкания)!

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::All/readdir, или File::Glob вместо:)

Ответ 2

Следующий код также создает два отдельных экземпляра итератора...

for ( 1..3 )
{
   $filename = <*>;
   print "$filename\n" if defined $filename;
   $filename = <*>;
   print "$filename\n" if defined $filename;
}

Я предполагаю, что вижу там логику, но это своего рода счетчик, интуитивный и противоречащий документации. В документах ничего не говорится о том, чтобы быть в цикле работы итерации.

Ответ 3

Также из perlop:

A (файл) glob оценивает свой (встроенный) аргумент только при запуске нового списка.

Вызов glob создает список, который либо возвращается целиком (в контексте списка), либо извлекается один элемент за раз (в скалярном контексте). Но каждый вызов glob создает отдельный список.

Ответ 4

(Царапина от моей ржавой памяти Perl...) Я думаю, что несколько лексических экземпляров <*> рассматриваются как независимые вызовы glob, тогда как в цикле while вы вызываете один и тот же "экземпляр" (независимо от того, что средства).

Представьте, например, если вы это сделали:

while (<*>) { ... }
...
while (<*>) { ... }

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