Discussion:
Perl: Sortieren nach "zweiter" Spalte
(zu alt für eine Antwort)
Andre Tann
2013-10-06 19:36:52 UTC
Permalink
Hallo zusammen,

folgende Frage zur Sortierfunktion in Perl:

Diese Dateien:

$ ls -1
prefix_1
prefix_11
prefix_12
prefix_14
prefix_2
prefix_3
prefix_5

möchte ich numerisch sortiert ausgeben. Die Bash macht es so:

$ ls -1 | sort -t_ -k2,2n
prefix_1
prefix_2
prefix_3
prefix_5
prefix_11
prefix_12
prefix_14

Wie bekomme ich das ähnlich elegant mit der sort-Funktion hin, ohne erst alles zu splitten, dann in ein zweidimensionales Array zu packen, dieses nach der zweiten Spalte zu sortieren, und dann wieder auszugeben?

Freue mich über Denkanstöße!
--
Andre Tann
Marcel Müller
2013-10-06 20:21:54 UTC
Permalink
Hallo,
Post by Andre Tann
Wie bekomme ich das ähnlich elegant mit der sort-Funktion hin, ohne erst alles zu splitten, dann in ein zweidimensionales Array zu packen, dieses nach der zweiten Spalte zu sortieren, und dann wieder auszugeben?
ich kenne auch keinen signifikant besseren Weg.

Du kannst natürlich mit einer Regex über die Zeilen rutschen, die die
zweite Spalte herausdestilliert und selbige im sort-comparer verwenden.
Ich glaube aber, dass das eher langsamer ist.

Also sinngemäß:

sub col2($)
{ $_[0] =~ /_([\d]*)/;
return $1;
}
sort { col2 $a <=> col2 $b } @data;

(geht wahrscheinlich auch noch irgendwie kürzer)


Marcel
Andre Tann
2013-10-06 20:31:43 UTC
Permalink
Hi Marcel,

vielen Dank für Dein Feedback.
Post by Marcel Müller
ich kenne auch keinen signifikant besseren Weg.
Du kannst natürlich mit einer Regex über die Zeilen rutschen, die die zweite Spalte herausdestilliert und selbige im sort-comparer verwenden. Ich glaube aber, dass das eher langsamer ist.
Mir käme es in diesem Fall gar nicht auf die Geschwindigkeit an, sondern auf die Lesbarkeit des Codes. Pro Durchlauf muß ich max. 30 Zeilen auf diese Weise sortieren, und ein Durchlauf dauert mehrere Stunden bis zu einer Woche. Ergo - die Laufzeit spielt überhaupt keine Rolle. Beste Lesbarkeit des Codes steht dafür ganz weit oben, und da finde ich den Aufruf "sort -t... -k..." schon sehr prägnant. Ich wollte nur möglichst nicht bash und perl vermischen, indem ich ein `sort ...` aufrufe. Aber gut, vielleicht läßt sichs hier nicht vermeiden.

Danke+Gruß!
--
Andre Tann
Bjoern Hoehrmann
2013-10-07 15:29:21 UTC
Permalink
Post by Andre Tann
Wie bekomme ich das ähnlich elegant mit der sort-Funktion hin, ohne erst
alles zu splitten, dann in ein zweidimensionales Array zu packen, dieses
nach der zweiten Spalte zu sortieren, und dann wieder auszugeben?
Eine Liste nach einem oder mehreren Sortierschlüsseln zu sortieren ist
ein so häufiges Problem, dass es dafür eigentlich eingebaute Funktionen
geben müsste. Leider ist das nicht der Fall, aber im CPAN gibt es viele
Module, die diese Lücke schliessen. Damit bekommt man den lesbarsten
Code. Ein Beispiel wäre ein List::OrderBy Modul,

% perl -MList::OrderBy -E "print order_by { s/^prefix_//r } <>"

gibt STDIN im von dir beschriebenem Format sortiert aus (wie man den
Schlüssel genau extrahieren muss, und was passieren soll, wenn kein
Schlüssel extrahiert werden kann, hast du nicht beschrieben).
--
Björn Höhrmann · mailto:***@hoehrmann.de · http://bjoern.hoehrmann.de
Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/
Andre Tann
2013-10-08 06:24:35 UTC
Permalink
Servus Björn,
Post by Bjoern Hoehrmann
Eine Liste nach einem oder mehreren Sortierschlüsseln zu sortieren ist
ein so häufiges Problem, dass es dafür eigentlich eingebaute Funktionen
geben müsste. Leider ist das nicht der Fall, aber im CPAN gibt es viele
Module, die diese Lücke schliessen. Damit bekommt man den lesbarsten
Code. Ein Beispiel wäre ein List::OrderBy Modul,
Tja, das Problem ist, daß a) die Büchsen, auf denen der sort stattfinden soll keinen Internet-Zugang haben. Und b) sind es mehrere Dutzend, sodaß ich auch ungern von Hand ein Modul einspiele.

Daher muß es mit den Mitteln laufen, die das Perl out of the box liefert. Blöde Einschränkungen, ich weiß, aber es ist halt so...
--
Andre Tann
Christian Garbs
2013-10-29 20:52:50 UTC
Permalink
Mahlzeit!
Post by Andre Tann
$ ls -1 | sort -t_ -k2,2n
Wie bekomme ich das ähnlich elegant mit der sort-Funktion hin, ohne
erst alles zu splitten, dann in ein zweidimensionales Array zu
packen, dieses nach der zweiten Spalte zu sortieren, und dann wieder
auszugeben?
Ich weiß, ich antworte spät, aber :-)

Da `perldoc -q sort` genau diese Variante für kompliziertere
Sortierfunktionen vorschlägt, sollte das temporäre zweidimensionale
Array passen. Und wenn es dort vorgeschlagen wird, dürfte es dann
auch unter "lesbar" im Sinne von "die offizielle Doku schlägt das
genau so vor, das hat man schon mal gesehen" fallen.

Dortiges Beispiel:

@idx = ();
for (@data) {
($item) = /\d+\s*(\S+)/;
push @idx, uc($item);
}
@sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];


Gruß
Christian
--
sub _{print"\n"}_;for(;$s<9;++$s){$_='1E2018201E00001E2018201E00001E2018201'
.'E002020001C2222221400005CA2A2A27C02001C2222221C20003E040202201F2422221C00'
.'242A2A2A12002020001C2222221F20001C2A2A2A0C';while(s;(..);;){printf'%c',hex
$1&1<<$s?40:32}_}$_=':::Christian Garbs:<***@cgarbs.de>',y;:;\t;;print;_;_
Ralf Döblitz
2013-10-30 18:43:09 UTC
Permalink
Post by Christian Garbs
Mahlzeit!
Post by Andre Tann
$ ls -1 | sort -t_ -k2,2n
Wie bekomme ich das ähnlich elegant mit der sort-Funktion hin, ohne
erst alles zu splitten, dann in ein zweidimensionales Array zu
packen, dieses nach der zweiten Spalte zu sortieren, und dann wieder
auszugeben?
Ich weiß, ich antworte spät, aber :-)
Da `perldoc -q sort` genau diese Variante für kompliziertere
Sortierfunktionen vorschlägt, sollte das temporäre zweidimensionale
Array passen. Und wenn es dort vorgeschlagen wird, dürfte es dann
auch unter "lesbar" im Sinne von "die offizielle Doku schlägt das
genau so vor, das hat man schon mal gesehen" fallen.
@idx = ();
($item) = /\d+\s*(\S+)/;
}
@sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];
Wenn es lesbar sein soll, dann bevorzuge ich da eher die Schwartzian
Transform:

my @sorted =
map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { my (undef, $key) = split(/_/); [$_, $key] }
@data;

Da das als Idiom auch bekannt genug ist, sollte jeder geübte
Perl-Programmierer sofort verstehen, worum es geht.

Ralf
--
"de.alt.comp.kde Dumm und trotzdem UNIX verwenden? KDE ist die Loesung."
  – Sven Paulus in <67hu21$43r$***@imperator.oops.sub.de>
Loading...