Hat Perl's Glob eine Einschränkung?

9

Ich führe die folgenden erwarteten Rückgabezeichenfolgen mit 5 Zeichen aus:

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

Es werden jedoch nur 4 Zeichen zurückgegeben:

anbc
anbd
anbe
anbf
anbg
...

Wenn ich jedoch die Anzahl der Zeichen in der Liste reduziere:

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

es kehrt korrekt zurück:

aamid
aamie
aamif
aamig
aamih
...

Kann mir bitte jemand sagen, was ich hier vermisse, gibt es eine Grenze? oder gibt es einen weg darum herum?

Wenn es einen Unterschied macht, gibt es in perl 5.26und das gleiche Ergebnis zurückperl 5.28

Gerry
quelle
Zuvor: stackoverflow.com/a/58852104 stackoverflow.com/a/58853045 Verwenden Sie ein Modul, das einen Iterator bereitstellt, anstatt die Glob-Funktion zu missbrauchen. p3rl.org/Algorithm::Combinatorics p3rl.org/Algorithm::Loops
daxim
Danke @daxim. Das Problem ist, dass ich gerade Probleme habe, Module jeglicher Art zu laden. Ich habe ein cpan-Problem, das sich über Win32 :: Console beschwert, aber ppm ist auch in Perl 5.28 nicht verfügbar, sodass ich das Modul laden kann, damit cpan aufhört, sich zu beschweren.
Gerry
Vielen Dank @zdim schätzen die Zeit und Mühe.
Gerry
Mir ist gerade klar geworden ... willst du das überhaupt gemischt (zufällig) oder nur die komplette Liste?
Zdim
@zdim nur eine vollständige Liste. :)
Gerry

Antworten:

6

Alles hat einige Einschränkungen.

Hier ist ein reines Perl-Modul, das dies iterativ für Sie erledigen kann. Es wird nicht die gesamte Liste auf einmal generiert und Sie erhalten sofort Ergebnisse:

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
quelle
Mann, du verstehst nicht, wie glücklich ich gerade bin. Vielen Dank!!
Gerry
3
Algorithmus :: Schleifen NestedLoopskönnten auch verwendet werden: use Algorithm::Loops qw( NestedLoops ); NestedLoops([ ([ 'a'..'z' ]) x 5 ], sub { say join '', @_ } ); (Eine Antwort auf eine frühere Frage des OP erwähnte, dass sie dies verwenden könnten, wenn ihnen der Speicher
ausgeht
8

Der globerste erstellt alle möglichen Dateinamenerweiterungen, sodass zuerst die vollständige Liste aus dem angegebenen Glob / Muster im Shell-Stil generiert wird. Nur dann wird es darüber iterieren, wenn es im skalaren Kontext verwendet wird. Deshalb ist es so schwer (unmöglich?), Dem Iterator zu entkommen, ohne ihn zu erschöpfen. siehe diesen Beitrag .

In Ihrem ersten Beispiel sind das 26 5 Zeichenfolgen ( 11_881_376), die jeweils fünf Zeichen lang sind. Also eine Liste von ~ 12 Millionen Zeichenfolgen mit einer (naiven) Summe von mehr als 56 MB ... plus dem Overhead für einen Skalar, der meiner Meinung nach mindestens 12 Bytes oder so beträgt. Also in der Größenordnung von 100 MB, zumindest genau dort in einer Liste.

Ich kenne keine formalen Beschränkungen für die Länge von Dingen in Perl (außer in Regex), aber globmacht das alles intern und es muss undokumentierte Beschränkungen geben - vielleicht werden einige Puffer irgendwo intern überlaufen? Es ist ein bisschen übertrieben.

Um dies zu umgehen, generieren Sie diese Liste mit 5-Zeichen-Zeichenfolgen iterativ, anstatt globihre Magie hinter den Kulissen rollen zu lassen . Dann sollte es absolut kein Problem geben.

Allerdings finde ich das Ganze auch in diesem Fall ein bisschen groß für Komfort. Ich würde wirklich empfehlen, einen Algorithmus zu schreiben, der jeweils ein Listenelement generiert und bereitstellt (einen "Iterator"), und damit zu arbeiten.

Es gibt gute Bibliotheken, die das können (und vieles mehr), von denen einige Algorithm :: Loops sind , die in einem früheren Beitrag zu diesem Thema (und in einem Kommentar), Algorithm :: Combinatorics (gleicher Kommentar), Set::CrossProductaus einer anderen Antwort empfohlen wurden Hier ...

Beachten Sie auch, dass globdie Bibliothek , obwohl dies eine clevere Verwendung ist, für die Arbeit mit Dateien gedacht ist. Abgesehen davon, dass es im Prinzip missbraucht wird, denke ich, dass es jeden der (~ 12 Millionen) Namen auf einen gültigen Eintrag überprüft ! (Siehe diese Seite .) Das ist eine Menge nicht benötigter Festplattenarbeit. (Und wenn Sie "Globs" wie *oder ?auf einigen Systemen verwenden, wird eine Liste mit nur Zeichenfolgen zurückgegeben, die tatsächlich Dateien enthalten, sodass Sie leise unterschiedliche Ergebnisse erhalten.)


 Ich erhalte 56 Bytes für eine Größe eines 5-Zeichen-Skalars. Während dies für eine deklarierte Variable gilt, die etwas mehr als einen anonymen Skalar benötigt, ist die tatsächliche Gesamtgröße im Testprogramm mit Zeichenfolgen der Länge 4 tatsächlich um eine gute Größenordnung größer als die naiv berechnete. Die Realität kann also in einem Arbeitsgang in der Größenordnung von 1 GB liegen.

Update   Ein einfaches Testprogramm, das diese Liste mit 5 Zeichen langen Zeichenfolgen (nach demselben globAnsatz) generiert, wurde 15 Minuten lang auf einem Computer der Serverklasse ausgeführt und benötigte 725 MB Speicher.

Auf diesem Server wurde die richtige Anzahl von 5 Zeichen langen Zeichenfolgen erzeugt, die scheinbar korrekt sind.

zdim
quelle
@Gerry Erstens bin ich mir nicht sicher, ob das Problem bei den Grenzen liegt. Ein Blick darauf werfen ... Vielleicht zuerst die Liste iterativ (nicht alle auf einmal) generieren und in einem geeigneten Array speichern? Das wird sicherlich keine Grenzen erreichen, eine "Handvoll" 5-Zeichen-Saiten. (Es ist auch diagnostisch - wenn das funktioniert, dann ist es tatsächlich eine interne Grenze.)
zdim
@Gerry Benötigen Sie keine Module - bauen Sie einfach die Liste (mit fünf Zeichenfolgen) zuerst Stück für Stück in ein Array ein, anstatt sie mit zusammenzufassen glob. (Das erfordert einen einfältigen, anderen Algorithmus. Vielleicht das, was ich in Ihrer vorherigen Frage gepostet habe? Das ist gutes Debugging - wenn Sie diese Liste ohne Probleme erhalten können, wissen Sie, dass hier Grenzwerte verschoben werden.) Ich habe einige Größenschätzungen hinzugefügt dass ich zur Post
komme
@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"... und lassen Sie mich überprüfen ... jetzt lief es in 30 Sekunden, was es bestätigt, wenn man bedenkt, wie das Cacheing hier funktioniert. Ich habe auch RSS mit externen Tools überprüft, während es lief.
Zdim
@ Gerry Gleiches Verhalten auf v5.29.2 (~ 600Mb jetzt) ​​... immer noch auf diesem Cache auf diesem Server fahren :)))
zdim
@Gerry Ergebnis von einem anderen Computer der Serverklasse mit Version 5.16 - 28 Minuten (während des Betriebs unterschätzt!) Und 750 MB. Jetzt erneut unter 5.29.2 und wieder ~ 600Mb. Korrigieren Sie die Zeichenfolgen und die richtige Anzahl (genau 26**5)
zdim