У Perl's Glob есть ограничение?

9

Я использую следующие ожидаемые строки возврата из 5 символов:

while (glob '{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}'x5) {
  print "$_\n";
}

но он возвращает только 4 символа:

anbc
anbd
anbe
anbf
anbg
...

Однако когда я уменьшу количество символов в списке:

while (glob '{a,b,c,d,e,f,g,h,i,j,k,l,m}'x5) {
  print "$_\n";
}

возвращается правильно:

aamid
aamie
aamif
aamig
aamih
...

Может кто-нибудь сказать мне, что мне здесь не хватает, есть ли какой-то предел? или есть ли способ обойти это?

Если это делает никакой разницы, она возвращает тот же результат в обоих perl 5.26иperl 5.28

Gerry
источник
Ранее: stackoverflow.com/a/58852104 stackoverflow.com/a/58853045 Используйте модуль, предоставляющий итератор, вместо злоупотребления функцией glob. p3rl.org/Algorithm::Combinatorics p3rl.org/Algorithm::Loops
daxim
Спасибо @daxim. Проблема в том, что я сейчас пытаюсь загрузить модули любого вида, у меня есть проблема cpan, жалующаяся на Win32 :: Console, но ppm также не доступен в perl 5.28, поэтому я могу загрузить модуль для cpan, чтобы перестать жаловаться.
Джерри
Спасибо @zdim ценим все время и усилия.
Джерри
Я только что понял ... Вы хотите, чтобы это было перемешано (рандомизировано) вообще, или просто полный список?
Здим
@zdim просто полный список. :)
Джерри

Ответы:

6

У всего есть некоторые ограничения.

Вот чистый модуль Perl, который может сделать это для вас итеративно. Он не генерирует весь список сразу, и вы сразу начинаете получать результаты:

use v5.10;

use Set::CrossProduct;

my $set = Set::CrossProduct->new( [ ([ 'a'..'z' ]) x 5 ] );

while( my $item = $set->get ) {
    say join '', @$item
    }
Брайан Д. Фой
источник
Человек, ты не понимаешь, как я счастлив сейчас. Большое спасибо!!
Джерри
3
Алгоритм :: Loops - х NestedLoopsтакже может быть использованы: use Algorithm::Loops qw( NestedLoops ); NestedLoops([ ([ 'a'..'z' ]) x 5 ], sub { say join '', @_ } ); (Ответ на предыдущий вопрос по ОП отметил , что они могли бы использовать это , если они выбегали из памяти ...)
Икеги
8

globПервым создает все возможные расширения имен файлов, поэтому он будет первым генерировать полный список из оболочки стиль Глоб / шаблона это дается. Только тогда он будет перебирать его, если он используется в скалярном контексте. Вот почему так трудно (невозможно?) Избежать итератора, не исчерпав его; см этого поста .

В вашем первом примере это 26 5 строк ( 11_881_376), каждая длиной пять символов. Итак, список из ~ 12 миллионов строк, с общим количеством (наивных) свыше 56 МБ ... плюс накладные расходы для скаляра, который, я думаю, составляет минимум 12 байтов или около того. Таким образом, порядка 100 МБ, по крайней мере, прямо в одном списке.

Я не знаю каких-либо формальных ограничений на длину вещей в Perl (кроме как в регулярных выражениях), но globделает ли это все внутренне и должны быть недокументированные ограничения - возможно, некоторые буферы где-то переполнены, внутренне? Это немного чрезмерно.

Что касается способа обойти это - генерировать этот список из 5-ти символьных строк итеративно, вместо того, чтобы позволять globсворачивать свою магию за кулисы. Тогда это абсолютно не должно иметь проблемы.

Тем не менее, я нахожу все это немного большим для комфорта, даже в этом случае. Я действительно рекомендую написать алгоритм, который генерирует и предоставляет один элемент списка за раз («итератор»), и работать с этим.

Есть хорошие библиотеки, которые могут сделать это (и многое другое), некоторые из которых Algorithm :: Loops, рекомендованные в предыдущем посте по этому вопросу (и в комментарии), Algorithm :: Combinatorics (тот же комментарий), Set::CrossProductиз другого ответа Вот ...

Также обратите внимание, что, хотя это разумное использование glob, библиотека предназначена для работы с файлами. Помимо неправильного использования в принципе, я думаю, что он проверит каждое из (~ 12 миллионов) имен на правильность записи ! (См. Эту страницу .) Это много ненужной работы с диском. (И если бы вы использовали «globs», как *или ?в некоторых системах, он возвращает список только с теми строками, которые действительно имеют файлы, так что вы спокойно получили бы другие результаты.)


 Я получаю 56 байтов за размер скаляра из 5 символов. В то время как это для объявленной переменной, которая может занять немного больше, чем анонимный скаляр, в тестовой программе со строками длины 4 фактический общий размер действительно на порядок больше, чем вычисляемый наивно. Так что реальная вещь вполне может быть порядка 1 Гб за одну операцию.

Обновление   Простая тестовая программа, которая генерирует этот список из 5-символьных длинных строк (используя тот же globподход), выполнялась в течение 15 минут на компьютере серверного класса и занимала 725 МБ памяти.

Это произвело правильное количество фактических 5-символьных длинных строк, казалось бы, правильных, на этом сервере.

zdim
источник
@ Джерри Во-первых, я не уверен, что проблема с ограничениями; изучая это ... Возможно, сначала сгенерируйте список, итеративно (не все сразу), и сохраните его в правильном массиве? Это, конечно, не получится нигде рядом, "горстка" 5-символьных строк. (Это также диагностика - если это работает, то это действительно некоторый внутренний предел.)
zdim
@ Джерри Не нужны модули - просто соберите список (из пятисимвольных строк) сначала в массив, по кусочкам, а не объединяйте его вместе, используя glob. (Для этого понадобится простой, другой алгоритм. Возможно, то, что я написал в вашем предыдущем вопросе? Это хорошая отладка - если вы можете получить этот список без проблем, тогда вы знаете, что здесь вводятся ограничения.) Я добавил некоторые оценки размера что я
добираюсь
@ Джерри time perl -MDevel::Size=total_size -wE'$chs = join ",", "a".."z"; @items = glob "{$chs}"x5; say STDERR "Total memory: ", total_size(\@items)/(1024**2), " Mb"... и позвольте мне проверить ... теперь он работает через 30 секунд, что подтверждает это, учитывая, как кеширование работает здесь. Я также проверил RSS с внешними инструментами, пока он шел.
Здим
@Gerry То же поведение на v5.29.2 (сейчас ~ 600Mb) ... все еще работает на этом кеше на этом сервере :)))
zdim
@Gerry Результат с другого компьютера серверного класса, с v5.16 - 28 минут (недооцененный во время работы!) И 750 МБ. Теперь перезапустить под 5.29.2 и снова ~ 600Mb. Правильные строки и правильное их количество (точно 26**5)
здим