2013-04-18 17 views
5

Wiem, że podobne tematy zostały omówione tutaj, ale mam problem, który moim zdaniem wynika z mojego nieporozumienia, jak wycinki tablic są interpolowane w kontekście pętli foreach. Nie mogę się dowiedzieć, gdzie się nie udało, więc szukam wglądu.Modyfikowanie, a następnie Krojenie tablicy dwuwymiarowej nieznanego rozmiaru w Perlu

Mam tablicę 2D ze zmienną liczbą rzędów. Na przykład celach:

@2DArray = (['str1', 1, 2, 'E', val1, val2, val3] 
      ['str2', 3, 4, 'E', val4, val5, val6] 
      ['str4', 5, 6, 'F', val7, val8, val9]) 

Chcę zbudować nową tablicę, z dodatkowych kolumn, które zawiera kilka rzędów oryginalnej tablicy tylko jeśli zawierają napis „E” w kolumnie 3. Dodatkowo, dla wiersze, które chcę wprowadzić w mojej nowej tablicy, chcę tylko podzbiór kolumn i chcę tego podzbioru w innej kolejności. Celem końcowym jest wygenerowanie danych wyjściowych o właściwym formacie wymaganym przez skrypty niższego szczebla.

Oto moja próba, aby to zrobić:

my $projName = 'test'; 

my $i = 1; 
my @Newarray 
my @Newarray_element; 
    foreach (@2DArray) { 
     if (${$_}[3] eq 'E') { 
      ${$_}[3] = $i; 
      ${$_}[5] = '+'; 
      @Newarray_element = ("$projName$i", @$_[0,1,2,5,3], 'STR', 11, 11); 
      $i++; 
      push (@Newarray, \@Newarray_element); 
     } 

     next; 
    } 

print (join("\t", @$_), "\n") for @Newarray; 

Jednakże, jeśli to zrobię, to, co pojawia się:

#(original) col nums:  0  1 2 5 3 

        test2 str2 3 4 + 2 STR 11 11 
        test2 str2 3 4 + 2 STR 11 11 

Ie, moja nowa tablica będzie mieć wiersz dla każdego wiersza w oryginalnej tablicy z literą "E" w kolumnie 3, ale każdy wiersz jest wypełniany wartościami z ostatniego wiersza, które mają być przetwarzane przez pętlę.

Powodem, dla którego myślę, że problem związany jest z cięciem tablicy 2D w pętli foreach, jest to, że wiem, że jeśli po prostu przejdę przez tablicę 2D, znajdę wszystkie wiersze z literą "E" w kolumnie 3, zmodyfikuj niektóre wartości w innych kolumnach dla tych wierszy, a następnie zwróć je do nowej tablicy - wszystko działa idealnie. To znaczy, jeśli zamiast tego zrobić:

my @Newarray; 
my $i = 1; 
foreach (@2Darray) { 
    if (${$_}[3] eq "E") { 
     ${$_}[3] = $i; 
     ${$_}[5] = '+'; 
     $i++; 
     push (@Newarray, \@$_); 
    } 
    next; 
} 
print (join("\t", @$_), "\n") for @Newarray; 

otrzymuję dokładnie wyjście spodziewałbym:

    *   & 
str1 1 2 1 val1 + val3 
str2 3 4 2 val4 + val6 

gdzie kolumny oznaczone * i & są zmodyfikowane kolumny 3 i 5. Niech zacznie się atak: skąd mój nowicjusz się nie pomylił?

Odpowiedz

4

Zmienna @Newarray_element wskazuje na tę samą przestrzeń pamięci w programie, więc zmiany wprowadzone w jednej iteracji są propagowane do poprzednich iteracji, w których użyto tej zmiennej w zadaniu.

Dwa ewentualne poprawki:

Jeden. Zmień zakres zmiennej tak, aby używała innej pamięci w każdej iteracji. Zmień

my @Newarray_element; 
foreach (@2DArray) { 
    ... 

do

foreach (@2DArray) { 
    my @Newarray_element; 
    ... 

lub nawet

foreach (@2DArray) { 
    ... 
    my @Newarray_element = ("$projName$i", @$_[0,1,2,5,3], 'STR', 11, 11); 

Dwa: ponowne @Newarray_element ale przypisać kopię swoich danych do każdego wiersza @Newarray.Zmień

push (@Newarray, \@Newarray_element); 

do

push (@Newarray, [ @Newarray_element ]); 

To ostatnie wezwanie tworzy i dołącza nowy, anonimowy odniesienie tablicę @Newarray.

+0

Dzięki. Bardzo głupi błąd i dowód, że nie opanowałem jeszcze referencji Perla. Czy możesz wytłumaczyć mi w swoim drugim przykładzie, co dokładnie robi [@New_element]? Jeśli czytam to poprawnie, czy tworzy to anonimową tablicę z zawartością @Newarray_element, która jest tracona po zamknięciu pętli? – MCor

+1

@MCor: Tak, '[@Newarray_element]' tworzy anonimową kopię treści '@ Newarray_element' (i zwraca odniesienie do tej kopii), tak aby po nadpisaniu' @ Newarray_element' przy następnej iteracji pętli kopia pozostanie oryginalna treść. (Jednak byłoby IMO znacznie czystsze, aby utworzyć nową lokalną tablicę w każdej iteracji, przenosząc deklarację 'my' wewnątrz pętli.) –

+1

' [...] 'tworzy nowe anonimowe odwołanie do tablicy, które zostanie wypchnięte na '@ Newarray'. Zawartość tej tablicy wskazanej przez to odwołanie będzie kopią rzeczy w '@ Newarray_element'. Ponieważ każdy jest kopią, nie ma znaczenia, czy zmieniasz '@ Newarray_element' za każdym razem w pętli. Ale pierwszą sugestią @ mob jest o wiele jaśniejszy i bardziej idiotyczny sposób na zrobienie tego. Ustalenie zmiennej do wnętrza pętli tworzy za każdym razem * nowy * '\ @ element Newarray'. – friedo

Powiązane problemy