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