2013-05-08 13 views
6

Poniższy fragment Perla ma na celu wydrukowanie pierwszych 5 elementów tablicy, do której odnosi się wartość skrótu, lub mniej, jeśli tablica jest krótsza.Nieoczekiwanie krótkie wycinki Perla

while (my ($key,$value) = each %groups) { 
    print "$key: \n"; 
    my @list = grep defined, @{$value}; 
    my @slice = grep defined, @list[0..4]; 
    foreach my $item (@slice) { 
     print " $item \n"; 
    } 
    print " (", scalar @slice, " of ", scalar @list, ")\n"; 
} 

Nie sądzę pierwszy grep defined jest to konieczne, ale nie może wyrządzić żadnej szkody, a powinien on zagwarantować, że nie istnieją żadne niezdefiniowane członkowie array przed plaster. Drugi grep defined jest usunięcie niezdefiniowanych elementów tablicy w wyniku slice gdy @list jest krótszy niż 5.

%groups została wypełniona przez wielokrotnych wywołań:

$groups{$key} =() unless defined $groups{$key}; 
    push @{$groups{$key}}, $value; 

Większość czasu działa dobrze:

key1: 
    value1 
    value2 
    value3 
    value4 
    value5 
    (5 of 100) 

Ale czasami - a ja nie pracowałem się, w jakich okolicznościach - widzę:

key2: 
    value1 
    (1 of 5) 

key3: 
    value1 
    value2 
    (2 of 5) 

Spodziewam długość listy drukowanej i x z (x of y) być min(5,y)

Co może być przyczyną takiego zachowania?

Odpowiedz

8

Używanie grep z wycinkiem tablicowym dla @list autovivifies elementów i rozszerza tablicę.

@foo = (1,2,3); 
@bar = @foo[0..9999]; 
print scalar @foo;    # => 3 

@foo = (1,2,3); 
@bar = grep 1, @foo[0..9999]; 
print scalar @foo;    # => 10000 

To samo dzieje się w innych kontekstach, w których Perl chce przechodzić przez wycinek tablicy.

@foo = (1,2,3); 
foreach (@foo[0..9999]) { } 
print scalar @foo;    # => 10000 

@foo = (1,2,3); 
@bar = map { } @foo[0..9999]; 
print scalar @foo;    # => 10000 

Więc jakie są obejścia?

  1. użycie bardziej skomplikowane wyrażenie zakresie lub grep argumentu

    @bar = grep 1, @foo[0..(@foo>=9999?9999:$#foo)]; 
    @bar = grep 1, @foo>=9999 ? @foo[0..9999] : @foo; 
    
  2. użycie zmiennej Tablica tymczasowy

    @bar = grep 1, @[email protected][0..9999] 
    
  3. (sugeruje @FMc) stosowanie map ustawić do macierzy pośredniej

    @bar = grep 1, map { $list[$_] } 0..9999; 
    
  4. praca z indeksów tablicy zamiast bezpośrednio z tablicy

    @bar_indices = grep defined($foo[$_]), 0..9999; 
    @bar = @foo[@bar_indices]; 
    
    @bar = @foo[ grep defined($foo[$_]), 0..9999 ]; 
    
+0

Ała! Czy istnieje wokół niego idiomatyczny (a przynajmniej zgrabny) sposób? – slim

+0

Myślę o tym. ["bez autoworyzacji"] (http://search.cpan.org/perldoc?autovivification) niestety nie pomaga. – mob

+0

'print" (", skalar @ slice," z ", skalar (grep defined, @list),") \ n ";' uzyskuje właściwe wyjście. Może zostawić jednak lukę dla przyszłych opiekunów. – slim

Powiązane problemy