Perl's Glob a-t-il une limitation?

9

J'utilise les chaînes de retour attendues de 5 caractères suivantes:

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";
}

mais il ne renvoie que 4 caractères:

anbc
anbd
anbe
anbf
anbg
...

Cependant, lorsque je réduis le nombre de caractères dans la liste:

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

il renvoie correctement:

aamid
aamie
aamif
aamig
aamih
...

Quelqu'un peut-il me dire ce qui me manque ici, y a-t-il une limite quelconque? ou y a-t-il un moyen de contourner cela?

Si cela fait une différence, il renvoie le même résultat à la fois perl 5.26etperl 5.28

Gerry
la source
Auparavant: stackoverflow.com/a/58852104 stackoverflow.com/a/58853045 Utilisez un module fournissant un itérateur au lieu d'abuser de la fonction glob. p3rl.org/Algorithm::Combinatorics p3rl.org/Algorithm::Loops
daxim
Merci @daxim. Le problème est que j'ai du mal à charger des modules de toute sorte en ce moment, j'ai un problème avec cpan qui se plaint de Win32 :: Console, mais ppm n'est pas disponible non plus en perl 5.28, donc je peux charger le module pour que cpan cesse de se plaindre.
Gerry
Merci @zdim apprécie tout le temps et les efforts.
Gerry
Je viens de réaliser ... voulez-vous que ce soit mélangé (randomisé), ou juste la liste complète?
zdim
@zdim juste une liste complète. :)
Gerry

Réponses:

6

Tout a une limite.

Voici un module Perl pur qui peut le faire de manière itérative. Il ne génère pas la liste entière à la fois et vous commencez à obtenir des résultats immédiatement:

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
la source
Mec, tu ne comprends pas à quel point je suis heureux en ce moment. Merci beaucoup!!
Gerry
3
Algorithm :: Loops NestedLoopspourrait également être utilisé: use Algorithm::Loops qw( NestedLoops ); NestedLoops([ ([ 'a'..'z' ]) x 5 ], sub { say join '', @_ } ); (Une réponse à une question précédente de l'OP mentionnait qu'ils pouvaient l'utiliser s'ils
manquaient
8

Le globpremier crée toutes les extensions de nom de fichier possibles, donc il va d'abord générer la liste complète à partir du glob / modèle de style shell qui lui est donné. Ce n'est qu'alors qu'il itérera dessus, s'il est utilisé dans un contexte scalaire. C'est pourquoi il est si difficile (impossible?) D'échapper à l'itérateur sans l'épuiser; voir ce post .

Dans votre premier exemple, c'est 26 5 chaînes ( 11_881_376), chacune de cinq caractères de long. Donc, une liste de ~ 12 millions de chaînes, avec un total (naïf) supérieur à 56 Mo ... plus les frais généraux pour un scalaire, qui je pense est au minimum de 12 octets ou autres. Donc, à l'ordre de 100 Mo, au moins, juste là dans une liste.

Je ne suis pas au courant de limites formelles sur la longueur des choses en Perl (sauf dans regex) mais globfait tout cela en interne et il doit y avoir des limites non documentées - peut-être que certains tampons sont dépassés quelque part, en interne? C'est un peu excessif.

Quant à un moyen de contourner cela - générez cette liste de chaînes de 5 caractères de manière itérative, au lieu de laisser globrouler sa magie dans les coulisses. Ensuite, cela ne devrait absolument pas avoir de problème.

Cependant, je trouve le tout un peu grand pour le confort, même dans ce cas. Je recommanderais vraiment d'écrire un algorithme qui génère et fournit un élément de liste à la fois (un "itérateur"), et de travailler avec cela.

Il existe de bonnes bibliothèques qui peuvent le faire (et beaucoup plus), dont certaines sont Algorithm :: Loops recommandées dans un article précédent à ce sujet (et dans un commentaire), Algorithm :: Combinatorics (même commentaire), à Set::CrossProductpartir d'une autre réponse ici ...

Notez également que, même s'il s'agit d'une utilisation intelligente de glob, la bibliothèque est conçue pour fonctionner avec des fichiers. En dehors de son utilisation abusive en principe, je pense qu'il vérifiera chacun (les ~ 12 millions) de noms pour une entrée valide ! (Voir cette page .) C'est beaucoup de travail disque inutile. (Et si vous deviez utiliser des "globes" comme *ou ?sur certains systèmes, il retourne une liste avec uniquement des chaînes qui ont réellement des fichiers, donc vous obtiendrez tranquillement des résultats différents.)


 J'obtiens 56 octets pour une taille de scalaire de 5 caractères. Bien que cela soit pour une variable déclarée, qui peut prendre un peu plus qu'un scalaire anonyme, dans le programme de test avec des chaînes de longueur 4, la taille totale réelle est en effet d'un bon ordre de grandeur plus grande que celle calculée naïvement. Donc, la vraie chose pourrait bien être de l'ordre de 1 Go, en une seule opération.

Mise à jour   Un programme de test simple qui génère cette liste de longues chaînes de 5 caractères (en utilisant la même globapproche) a fonctionné pendant 15 minutes sur une machine de classe serveur et a pris 725 Mo de mémoire.

Il a produit le bon nombre de chaînes longues de 5 caractères, apparemment correctes, sur ce serveur.

zdim
la source
@Gerry D'abord, je ne suis pas sûr que le problème soit avec des limites; en y regardant ... Peut-être générer d'abord la liste, de manière itérative (pas tout à la fois), et la stocker dans un tableau approprié? Cela n'atteindra certainement pas les limites, une "poignée" de chaînes de 5 caractères. (C'est aussi un diagnostic --- si cela fonctionne, c'est en effet une limite interne.)
zdim
@Gerry N'avez pas besoin de modules --- construisez simplement la liste (des chaînes de cinq caractères) dans un tableau d'abord, morceau par morceau, au lieu de les regrouper en utilisant glob. (Cela nécessitera un autre algorithme simple. Peut-être ce que j'ai posté dans votre question précédente? C'est un bon débogage - si vous pouvez obtenir cette liste sans problème, vous savez que des limites sont repoussées ici.) J'ai ajouté des estimations de taille que j'arrive au poste ...
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"... et laissez-moi vérifier ... maintenant, il a fonctionné en 30 secondes, ce qui le confirme étant donné le fonctionnement du cache ici. J'ai également vérifié RSS avec des outils externes pendant qu'il se déroulait.
zdim
@Gerry Même comportement sur v5.29.2 (~ 600 Mo maintenant) ... toujours sur ce cache sur ce serveur :)))
zdim
@Gerry Résultat d'une autre machine de classe serveur, avec v5.16 - 28 minutes (sous-estimé pendant son fonctionnement!) Et 750 Mo. Réexécutez maintenant sous 5.29.2 et encore ~ ​​600Mb. 26**5
Chaînes