O Glob de Perl tem uma limitação?

9

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.26eperl 5.28

Gerry
fonte
Anteriormente: stackoverflow.com/a/58852104 stackoverflow.com/a/58853045 Use um módulo que forneça um iterador em vez de abusar da função glob. p3rl.org/Algorithm::Combinatorics p3rl.org/Algorithm::Loops
daxim
Obrigado @daxim. O problema é que estou lutando para carregar módulos de qualquer tipo agora, tenho um problema cpan reclamando do Win32 :: Console, mas o ppm não está disponível no perl 5.28 para que eu possa carregar o módulo para que o cpan pare de reclamar.
Gerry
Obrigado @zdim aprecio todo o tempo e esforço.
Gerry
Acabei de perceber ... você quer isso aleatoriamente, ou apenas a lista completa?
Zdim
@zdim apenas uma lista completa. :)
Gerry

Respostas:

6

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:

use v5.10;

use Set::CrossProduct;

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

while( my $item = $set->get ) {
    say join '', @$item
    }
brian d foy
fonte
Cara, você não entende como estou feliz agora. Muito obrigado!!
Gerry
3
Algorithm :: Laços de NestedLoopstambé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 ...)
Ikegami
8

O globprimeiro 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 globtudo 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 globrolar 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::CrossProductde 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 globabordagem) 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.

zdim
fonte
@ Greg Primeiro, não tenho certeza se o problema está nos limites; olhando para ele ... Talvez gere a lista primeiro, iterativamente (não de uma só vez), e armazene-a em uma matriz adequada? Isso certamente não chegará nem perto de limites, um "punhado" de sequências de 5 caracteres. (É também de diagnóstico --- se isso funciona, então é de fato algum limite interno.)
zdim
@ Gerry Não precisa de módulos - basta criar a lista (de cinco caracteres) em uma matriz primeiro, peça por peça, em vez de agrupá-la usando 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 ...
zdim
@ Greg 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.
zdim
@Gerry mesmo comportamento em v5.29.2 (~ 600 MB agora) ... ainda montando em que o cache neste servidor :)))
zdim
@Gerry Resultado de outra máquina da classe de servidor, com v5.16 - 28 minutos (subestimado enquanto estava em andamento!) E 750Mb. Agora reran sob 5.29.2 e novamente ~ 600Mb. Cordas correta e número correto deles (exatamente 26**5)
zdim