Czy Glob Perla ma jakieś ograniczenia?

9

Korzystam z następujących oczekujących ciągów znaków o długości 5 znaków:

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

ale zwraca tylko 4 znaki:

anbc
anbd
anbe
anbf
anbg
...

Kiedy jednak zmniejszę liczbę znaków na liście:

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

zwraca poprawnie:

aamid
aamie
aamif
aamig
aamih
...

Czy ktoś może mi powiedzieć, czego tu brakuje, czy istnieje jakiś limit? czy jest na to sposób?

Jeśli robi jakąkolwiek różnicę, zwraca ten sam wynik zarówno w, jak perl 5.26iperl 5.28

Gerry
źródło
Poprzednio: stackoverflow.com/a/58852104 stackoverflow.com/a/58853045 Użyj modułu udostępniającego iterator zamiast nadużywać funkcji glob. p3rl.org/Algorytm::Combinatorics p3rl.org/Algorytm::Loops
daxim
Dzięki @daxim. Problem polega na tym, że walczę teraz o ładowanie modułów jakiegokolwiek rodzaju, mam problem cpan narzekający na Win32 :: Console, ale ppm nie jest dostępny w perl 5.28, więc mogę załadować moduł, aby cpan przestał narzekać.
Gerry
Dzięki @zdim doceniam cały czas i wysiłek.
Gerry
Właśnie zdałem sobie sprawę ... czy w ogóle chcesz to przetasować (losowo), czy po prostu pełną listę?
zdim
@zdim tylko pełna lista. :)
Gerry

Odpowiedzi:

6

Wszystko ma pewne ograniczenia.

Oto czysty moduł Perla, który może zrobić to iteracyjnie. Nie generuje całej listy naraz i natychmiast uzyskujesz wyniki:

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
źródło
Człowieku, nie rozumiesz, jak szczęśliwy jestem teraz. Dziękuję Ci bardzo!!
Gerry
3
Algorytm :: Pętle NestedLoopsmogą być również użyte: use Algorithm::Loops qw( NestedLoops ); NestedLoops([ ([ 'a'..'z' ]) x 5 ], sub { say join '', @_ } ); (Odpowiedź na wcześniejsze pytanie OP wspomniała, że ​​mogliby tego użyć, gdyby zabrakło pamięci ...)
ikegami
8

globNajpierw tworzy wszystkie możliwe rozszerzenia nazw plików, więc będzie najpierw wygenerować pełną listę z powłoki stylu glob / wzór jest dany. Tylko wtedy będzie iterować, jeśli zostanie użyte w kontekście skalarnym. Dlatego tak trudno (nie?) Uciec z iteratora bez jego wyczerpania; zobacz ten post .

W pierwszym przykładzie jest to 26 5 ciągów znaków ( 11_881_376), każdy o długości pięciu znaków. Więc lista ~ 12 milionów łańcuchów, z (naiwnym) sumą przekraczającą 56 Mb ... plus koszty ogólne dla skalara, który moim zdaniem wynosi co najmniej 12 bajtów. Tak więc, co najmniej 100 Mb, przynajmniej na jednej liście.

Nie znam żadnych formalnych ograniczeń długości rzeczy w Perlu (innych niż regex), ale globczy to wszystko wewnętrznie i muszą istnieć nieudokumentowane ograniczenia - być może niektóre bufory są gdzieś przekroczone, wewnętrznie? To trochę przesada.

Jeśli chodzi o obejście tego - wygeneruj iteracyjnie tę listę ciągów 5-znakowych, zamiast pozwolić globrzucać swoją magią za kulisy. To absolutnie nie powinno mieć problemu.

Jednak uważam, że to wszystko jest nieco duże dla wygody, nawet w tym przypadku. Naprawdę polecam napisać algorytm, który generuje i udostępnia jeden element listy na raz („iterator”), i pracuję z tym.

Istnieją dobre biblioteki, które potrafią to zrobić (i wiele więcej), z których niektóre to Algorytm :: Pętle zalecane w poprzednim poście na ten temat (i w komentarzu), Algorytm :: Kombinatoryka (ten sam komentarz), Set::CrossProductz innej odpowiedzi tutaj ...

Zauważ też, że chociaż jest to sprytne zastosowanie glob, biblioteka jest przeznaczona do pracy z plikami. Oprócz nadużycia w zasadzie myślę, że sprawdzi każde z (~ 12 milionów) nazw pod kątem prawidłowego wpisu ! (Zobacz tę stronę .) To dużo niepotrzebnej pracy na dysku. (A jeśli użyjesz „globów”, takich jak *lub ?w niektórych systemach, zwraca listę zawierającą tylko ciągi znaków, które faktycznie mają pliki, więc po cichu uzyskasz inne wyniki.)


 Dostaję 56 bajtów dla rozmiaru 5-znakowego skalara. Chociaż dotyczy to deklarowanej zmiennej, która może zająć nieco więcej niż anonimowy skalar, w programie testowym z łańcuchami o długości 4 rzeczywisty całkowity rozmiar jest rzeczywiście o rząd wielkości większy niż naiwnie obliczony. Tak więc rzeczywiste może być rzędu 1 Gb w jednej operacji.

Aktualizacja   Prosty program testowy, który generuje tę listę łańcuchów o długości 5 znaków (stosując to samo globpodejście), działał przez 15 minut na komputerze klasy serwerowej i zajął 725 Mb pamięci.

Na tym serwerze wytworzono odpowiednią liczbę rzeczywistych łańcuchów o długości 5 znaków, pozornie poprawnych.

zdim
źródło
@Gerry Po pierwsze, nie jestem pewien, czy problem dotyczy limitów; patrząc na to ... Może najpierw wygenerujesz listę iteracyjnie (nie wszystkie naraz) i zapiszesz ją we właściwej tablicy? To z pewnością nie zbliży się do żadnych granic, „garści” ciągów 5-znakowych. (Jest to również diagnostyczne --- jeśli to działa, to rzeczywiście jest jakiś wewnętrzny limit.)
zdim
@Gerry Nie potrzebujesz modułów --- po prostu zbuduj listę (ciągów pięcioznakowych) najpierw w tablicy, kawałek po kawałku, zamiast zlepiać ją razem glob. (Będzie to wymagało prostego, innego algorytmu. Być może to, co napisałem w poprzednim pytaniu? To dobre debugowanie - jeśli możesz uzyskać tę listę bez problemów, wiesz, że limity są tutaj przesuwane.) Dodałem szacunkowe rozmiary że dostaję się na pocztę ...
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"... i pozwól mi sprawdzić ... teraz uruchomiono go za 30 sekund, co potwierdza, biorąc pod uwagę, jak działa tutaj buforowanie. Sprawdziłem też RSS za pomocą zewnętrznych narzędzi, gdy było w ruchu.
zdim
@Gerry Takie samo zachowanie w wersji v.2.2.2 (teraz ~ 600 MB) ... nadal jedzie na tej pamięci podręcznej na tym serwerze :)))
zdim
@Gerry Wynik z innej maszyny klasy serwerowej, z v5.16 - 28 minut (niedoszacowany podczas pracy!) I 750Mb. Teraz przenieś poniżej 5.29.2 i ponownie ~ 600 Mb. Prawidłowe ciągi i poprawna ich liczba (dokładnie 26**5)
zdim