¿Perl's Glob tiene una limitación?

9

Estoy ejecutando las siguientes cadenas de retorno 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";
}

pero solo devuelve 4 caracteres:

anbc
anbd
anbe
anbf
anbg
...

Sin embargo, cuando reduzco el número de caracteres en la lista:

while (glob '{a,b,c,d,e,f,g,h,i,j,k,l,m}'x5) {
  print "$_\n";
}

vuelve correctamente:

aamid
aamie
aamif
aamig
aamih
...

¿Alguien puede decirme qué me estoy perdiendo aquí, hay algún tipo de límite? o hay alguna forma de evitar esto?

Si hace alguna diferencia, devuelve el mismo resultado en ambos perl 5.26yperl 5.28

Gerry
fuente
Anteriormente: stackoverflow.com/a/58852104 stackoverflow.com/a/58853045 Use un módulo que proporcione un iterador en lugar de abusar de la función glob. p3rl.org/Algorithm::Combinatorics p3rl.org/Algorithm::Loops
daxim
Gracias @daxim. El problema es que estoy luchando para cargar módulos de cualquier tipo en este momento, tengo un problema de cpan quejándose de Win32 :: Console, pero ppm tampoco está disponible en perl 5.28, así que puedo cargar el módulo para que cpan deje de quejarse.
Gerry
Gracias @zdim aprecio todo el tiempo y esfuerzo.
Gerry
Me acabo de dar cuenta ... ¿quieres que esto se baraje (al azar) o solo la lista completa?
zdim
@zdim solo una lista completa. :)
Gerry

Respuestas:

6

Todo tiene alguna limitación.

Aquí hay un módulo de Perl puro que puede hacerlo de forma iterativa. No genera la lista completa a la vez y comienza a obtener resultados de inmediato:

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
fuente
Hombre, no entiendes lo feliz que estoy ahora. ¡¡Muchas gracias!!
Gerry
3
Algoritmo :: Los bucles NestedLoopstambién podrían usarse: use Algorithm::Loops qw( NestedLoops ); NestedLoops([ ([ 'a'..'z' ]) x 5 ], sub { say join '', @_ } ); (Una respuesta a una pregunta anterior del OP mencionó que podrían usar esto si se estuvieran quedando sin memoria ...)
ikegami
8

El globprimero crea todas las expansiones posibles de nombre de archivo, por lo que primero generará la lista completa del patrón / globo de estilo shell que se le da. Solo entonces iterará sobre él, si se usa en contexto escalar. Por eso es tan difícil (¿imposible?) Escapar del iterador sin agotarlo; ver este post .

En su primer ejemplo, son 26 5 cadenas ( 11_881_376), cada cinco caracteres de largo. Entonces, una lista de ~ 12 millones de cadenas, con un total (ingenuo) superior a 56Mb ... más la sobrecarga para un escalar, que creo que como mínimo es de 12 bytes o más. Entonces, en el orden de 100Mb, como mínimo, justo allí en una lista.

No conozco ningún límite formal sobre la longitud de las cosas en Perl (que no sea en expresiones regulares), pero glob¿todo eso internamente y debe haber límites indocumentados, tal vez algunos búferes se desbordan internamente? Es un poco excesivo.

En cuanto a una forma de evitar esto, genere esa lista de cadenas de 5 caracteres de forma iterativa, en lugar de dejar que globsu magia entre bastidores. Entonces absolutamente no debería tener un problema.

Sin embargo, encuentro todo un poco grande para la comodidad, incluso en ese caso. Realmente recomendaría escribir un algoritmo que genere y proporcione un elemento de lista a la vez (un "iterador"), y trabaje con eso.

Hay buenas bibliotecas que pueden hacer eso (y muchas más), algunas de las cuales son Algorithm :: Loops recomendadas en una publicación anterior sobre este asunto (y en un comentario), Algorithm :: Combinatorics (mismo comentario), Set::CrossProductde otra respuesta aquí ...

También tenga en cuenta que, si bien este es un uso inteligente de glob, la biblioteca está diseñada para trabajar con archivos. Además de mal uso en principio, ¡creo que verificará cada uno de los (los ~ 12 millones) nombres para una entrada válida ! (Consulte esta página ). Eso es mucho trabajo de disco innecesario. (Y si tuviera que usar "globos" como *o ?en algunos sistemas, devuelve una lista con solo cadenas que realmente tienen archivos, por lo que silenciosamente obtendría resultados diferentes).


 Obtengo 56 bytes para un tamaño de escalar de 5 caracteres. Si bien eso es para una variable declarada, que puede tomar un poco más que un escalar anónimo, en el programa de prueba con cadenas de longitud 4, el tamaño total real es de hecho un orden de magnitud mayor que el calculado ingenuamente. Entonces, lo real puede ser del orden de 1 Gb, en una sola operación.

Actualización   Un programa de prueba simple que genera esa lista de cadenas largas de 5 caracteres (con el mismo globenfoque) se ejecutó durante 15 minutos en una máquina de clase servidor y tomó 725 Mb de memoria.

Produjo el número correcto de cadenas largas reales de 5 caracteres, aparentemente correcta, en este servidor.

zdim
fuente
@Gerry Primero, no estoy seguro de que el problema sea con los límites; investigándolo ... ¿Quizás generar primero la lista, de forma iterativa (no de una vez) y almacenarla en una matriz adecuada? Eso seguramente no se acercará a ningún límite, un "puñado" de cadenas de 5 caracteres. (También es diagnóstico --- si eso funciona, entonces es un límite interno.)
zdim
@Gerry No necesita módulos --- solo construya la lista (de cadenas de cinco caracteres) en una matriz primero, pieza por pieza, en lugar de agruparla usando glob. (Eso necesitará algún otro algoritmo de mente simple. ¿Quizás lo que publiqué en su pregunta anterior? Esa es una buena depuración: si puede obtener esa lista sin problemas, entonces sabe que los límites se están presionando aquí). Agregué algunas estimaciones de tamaño que estoy llegando a la publicación ...
zdim
@Gerry 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"... y déjame comprobar ... ahora se ejecutó en 30 segundos, lo que confirma dado el funcionamiento del almacenamiento en caché aquí. También revisé RSS con herramientas externas mientras estaba funcionando.
zdim
@Gerry Mismo comportamiento en v5.29.2 (~ 600Mb ahora) ... todavía en ese caché en este servidor :)))
zdim
@Gerry Resultado de otra máquina de clase de servidor, con v5.16 - 28 minutos (¡subestimado mientras estaba en marcha!) Y 750Mb. Ahora vuelva a ejecutar bajo 5.29.2 y nuevamente ~ 600Mb. Cadenas correctas y el número correcto de ellas (exactamente 26**5)
zdim