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


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
    }

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.


@ 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.