Estou executando as seguintes seqüências de retorno esperadas de 5 caracteres:
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";
}
mas retorna apenas 4 caracteres:
anbc
anbd
anbe
anbf
anbg
...
No entanto, quando reduzo o número de caracteres na lista:
while (glob '{a,b,c,d,e,f,g,h,i,j,k,l,m}'x5) {
print "$_\n";
}
retorna corretamente:
aamid
aamie
aamif
aamig
aamih
...
Alguém pode me dizer o que estou perdendo aqui, há algum tipo de limite? ou existe uma maneira de contornar isso?
Se faz alguma diferença, retorna o mesmo resultado em ambos perl 5.26
eperl 5.28
Respostas:
Tudo tem alguma limitação.
Aqui está um módulo Perl puro que pode fazer isso iterativamente. Não gera a lista inteira de uma só vez e você começa a obter resultados imediatamente:
fonte
NestedLoops
também poderia ser usado:use Algorithm::Loops qw( NestedLoops ); NestedLoops([ ([ 'a'..'z' ]) x 5 ], sub { say join '', @_ } );
(Uma resposta a uma pergunta anterior pelo OP mencionou que eles poderiam usar isso se eles estavam ficando sem memória ...)O
glob
primeiro cria todas as expansões possíveis de nomes de arquivos, para gerar primeiro a lista completa a partir do padrão / glob no estilo de shell que é fornecido. Somente então ele irá iterar sobre ele, se usado em contexto escalar. É por isso que é tão difícil (impossível?) Escapar do iterador sem esgotá-lo; veja este post .No seu primeiro exemplo, são 26 5 strings (
11_881_376
), cada uma com cinco caracteres. Portanto, uma lista de ~ 12 milhões de strings, com um total (ingênuo) superior a 56Mb ... mais a sobrecarga de um escalar, que eu acho que é no mínimo 12 bytes ou mais. Então, na ordem dos 100Mb, no mínimo, ali mesmo em uma lista. †Eu não estou ciente de quaisquer limites formais no tamanho das coisas no Perl (exceto no regex), mas
glob
tudo isso internamente e deve haver limites não documentados - talvez alguns buffers sejam invadidos em algum lugar, internamente? É um pouco excessivo.Como uma maneira de contornar isso - gere a lista de sequências de 5 caracteres de forma iterativa, em vez de deixar
glob
rolar sua mágica nos bastidores. Então absolutamente não deve ter um problema.No entanto, acho a coisa toda um pouco grande para o conforto, mesmo nesse caso. Eu realmente recomendo escrever um algoritmo que gere e forneça um elemento de lista por vez (um "iterador") e trabalhe com isso.
Existem boas bibliotecas que podem fazer isso (e muito mais), algumas das quais são Algorithm :: Loops recomendados em um post anterior sobre esse assunto (e em um comentário), Algorithm :: Combinatorics (mesmo comentário),
Set::CrossProduct
de outra resposta aqui ...Observe também que, embora este seja um uso inteligente
glob
, a biblioteca deve funcionar com arquivos. Além de usá-lo mal em princípio, acho que ele verificará cada um dos (12 milhões) nomes para uma entrada válida ! (Veja esta página .) É muito trabalho em disco desnecessário. (E se você fosse usar "bolhas" como*
ou?
em alguns sistemas ele retorna uma lista com apenas cordas que realmente têm arquivos, assim você iria tranquilamente obter resultados diferentes.)† Estou recebendo 56 bytes para o tamanho de um escalar de 5 caracteres. Enquanto isso é para uma variável declarada, que pode demorar um pouco mais do que um escalar anônimo, no programa de teste com seqüências de comprimento 4, o tamanho total real é de fato uma boa ordem de magnitude maior que a calculada ingenuamente. Portanto, a coisa real pode muito bem estar na ordem de 1 GB, em uma operação.
Atualização Um programa de teste simples que gera essa lista de sequências longas de 5 caracteres (usando a mesma
glob
abordagem) foi executado por 15 minutos em uma máquina de classe de servidor e consumiu 725 Mb de memória.Ele produziu o número certo de strings reais de 5 caracteres, aparentemente corretas, neste servidor.
fonte
glob
. (Isso vai exigir algum algoritmo simplista e outro. Talvez o que eu postei na sua pergunta anterior? Isso é bom para depuração - se você pode obter essa lista sem problemas, sabe que os limites estão sendo ultrapassados aqui.) Adicionei algumas estimativas de tamanho que estou recebendo para o cargo ...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"
... e deixe-me verificar ... agora ele foi executado em 30 segundos, o que confirma, dado o funcionamento do cache aqui. Também verifiquei o RSS com ferramentas externas enquanto estava em andamento.26**5
)