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.26
yperl 5.28
Respuestas:
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:
fuente
NestedLoops
tambié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 ...)El
glob
primero 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
glob
su 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::CrossProduct
de 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
glob
enfoque) 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.
fuente
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 ...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.26**5
)