我正在运行以下预期返回的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
中返回相同的结果
答案 0 :(得分:8)
glob
首先创建所有可能的文件扩展名,因此它将首先从给出的shell样式的glob /模式中生成完整列表。如果在标量上下文中使用,则只有这样才能对其进行迭代。这就是为什么要逃避迭代器而不用尽它是如此困难(不可能?)的原因。参见this post。
在您的第一个示例中,它是26个 5 字符串(11_881_376
),每个字符串长5个字符。因此,大约有1200万个字符串的列表,(原始)总数超过56Mb ...加上标量的开销,我认为标量的开销至少为12个字节左右。因此,至少100Mb的数量级就在一个列表中。†
我不知道Perl(正则表达式中除外)对事物长度的任何正式限制,但是glob
在内部进行所有操作,并且必须有未记录的限制-也许某些缓冲区在内部某个地方溢出了?有点过分了。
关于此问题的一种解决方法-迭代生成5个字符的字符串列表,而不是让glob
发挥其魔力。那么它绝对不应该有问题。
但是,即使在那种情况下,我发现整件事对于舒适性来说还是有点大。我真的建议编写一种算法,一次生成并提供一个列表元素(“迭代器”),然后使用该算法。
有很好的库可以做到这一点(以及更多),其中一些Algorithm::Loops在上一则有关此问题的文章中(并在评论中)推荐,Algorithm::Combinatorics(相同的评论),Set::CrossProduct
来自另一个答案……
还要注意,尽管这是glob
的巧妙用法,但该库是用于处理文件的。除了原则上滥用它之外,我认为它会检查(〜1200万个)名称中的每一个以获取有效条目! (请参阅this page。)这是许多不需要的磁盘工作。 (而且,如果您在某些系统上使用*
或?
之类的“字符串”,它将返回仅包含实际包含文件的字符串的列表,因此您会悄悄地获得不同的结果。)
†我正在获得56个字节的5个字符的标量。尽管这是一个声明的变量,它可能比匿名标量花费更多,但是在具有长度为4的字符串的测试程序中,实际总大小确实比幼稚计算的大小大一个数量级。因此,一次操作中,真实的东西很可能约为1Gb。
更新。一个简单的测试程序(使用相同的glob
方法生成5字符长字符串的列表)在服务器级计算机上运行了15分钟, 725 Mb的内存。
在此服务器上,它确实生成了正确数量的看似正确的5字符长字符串。
答案 1 :(得分: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
}