2012-01-13 7 views
13

Mam tablicę tablic, które chcę posortować. Każdy element tablicy A jest tablicą z 3 elementami. tablicy A wygląda następująco:Chcę sortować tablicę tablic w Perlu, ale wynik nie jest sortowany

my @A = ([2,3,1], [1,2,3], [1,0,2], [3,1,2], [2,2,4]); 

chcę sortować w porządku rosnącym. Porównując 2 elementy, używana jest pierwsza liczba. W przypadku remisu używa się drugiego numeru, a następnie trzeciego numeru.

Oto mój kod. Używam funkcji "cmpfunc" do porównania 2 elementów.

sub cmpfunc { 
    return ($a->[0] <=> $b->[0]) or 
      ($a->[1] <=> $b->[1]) or 
      ($a->[2] <=> $b->[2]); 
} 
my @B = sort cmpfunc @A; 
print "Result:\n"; 
for my $element (@B) { 
    print join(",", @{$element}) . "\n"; 
} 

Wynik:

1,2,3 
1,0,2 
2,3,1 
2,2,4 
3,1,2 

Wynik jest nieco sortowane, ale nie są poprawne. Oczekuję:

1,0,2 
1,2,3 
2,2,4 
2,3,1 
3,1,2 

Czy jest jakiś błąd w mojej funkcji porównania? Dziwne jest to, że kiedy umieszczam kod porównania w bloku, wynik jest poprawnie sortowany.

my @C = sort { ($a->[0] <=> $b->[0]) or 
       ($a->[1] <=> $b->[1]) or 
       ($a->[2] <=> $b->[2]) } @A; 
+0

Powiązane: http://stackoverflow.com/questions/1512547 – mob

Odpowiedz

21

Jesteś wykonywania

return ($a->[0] <=> $b->[0]) 

która zwraca zanim dojdzie do jakiejkolwiek z "lub" klauzul.

Albo usunąć „powrót” słowo kluczowe, lub dodać nawiasów wokół całej listy arg powrotu:

sub cmpfunc { 
    return(($a->[0] <=> $b->[0]) or 
      ($a->[1] <=> $b->[1]) or 
      ($a->[2] <=> $b->[2])); 
} 
+8

* LUB * użyj wiązania mocniejszego lub: '||'. – Axeman

5

potrzebuje więcej nawiasów:

sub cmpfunc { 
    return (($a->[0] <=> $b->[0]) or 
      ($a->[1] <=> $b->[1]) or 
      ($a->[2] <=> $b->[2])); 
} 
9

Powodem przestrzegać tego „złego” zachowania jest priorytetem or operatora, najniższy możliwy. W tej sytuacji oznacza to, że

return ($a->[0] <=> $b->[0]) or 
     ($a->[1] <=> $b->[1]) or 
     ($a->[2] <=> $b->[2]); 

jest interpretowany jako OR-ing

return ($a->[0] <=> $b->[0]) 

i resztę linii - bzdury w tym przypadku, jak powrót nigdy nie wraca. :)

Więc należy użyć c lub:

return ($a->[0] <=> $b->[0]) || 
     ($a->[1] <=> $b->[1]) || 
     ($a->[2] <=> $b->[2]); 
+1

Dzięki, || to dobra alternatywa. – jftsai

3
sub cmpfunc { 
    return ($a->[0] <=> $b->[0]) or 
      ($a->[1] <=> $b->[1]) or 
      ($a->[2] <=> $b->[2]); 
} 

można usunąć 'powrót' tutaj.

sub cmpfunc { 
    ($a->[0] <=> $b->[0]) or 
    ($a->[1] <=> $b->[1]) or 
    ($a->[2] <=> $b->[2]); 
} 
+0

To nadal będzie zwracało pierwsze zdanie, które zwraca wartość true. –

+1

@LeonardoHerrera To ma zrobić. – TLP

+0

@TLP - doh, masz rację. –

2

Alternatywnym rozwiązaniem do Daniela:

sub cmpfunc { 
    return ($a->[0] <=> $b->[0]) || 
      ($a->[1] <=> $b->[1]) || 
      ($a->[2] <=> $b->[2]); 
} 

Problem z or tym przypadku jest to, że ma niższy priorytet niż zadania, więc funkcja zwraca tylko wynik ($a->[0] <=> $b->[0]), czyli -1, 0 lub 1, jeśli lewa strona jest odpowiednio mniejsza niż, równa lub większa niż prawa strona.|| ma wyższy priorytet, więc całe wyrażenie boolowskie jest oceniane przed powrotem. Jak wspomniano, możesz zawrzeć wyrażenie w nawiasach, jeśli wolisz, aby to było ||. Osobiście nie.

+0

W rzeczywistości zwraca tylko pierwsze porównanie, bez względu na to, co zwraca. Spróbuj 'sub a {return 0 or die" Ough "}'. – TLP

+0

@TLP: Dzięki za wskazanie tego. – flesk

Powiązane problemy