André Muschter: Sortierung von ungarischen Vokalen

Hallo miteinander, und schönes Pfingstfest, ;)

In der ungarischen Sprache gibt es sogenannte lange Vokale, die durch das Schreiben des "normalen" Vokales und dem Draufsetzen eines Akzentres gebildet werden.
So entstehen zu den kurzen Vokalen a,e,i,o,u,ö,ü die langen Vokale á,é,í,ó,ú,õ,û - jeweils in Klein- und Großschreibweise.

Die langen Vokale werden nun in dieser Sprache - z.B. in Wörterbüchern - direkt hinter die kurzen eingeordnet.
Die Reihenfolge ist also:

A Á B C D E É F G H I Í ... N O Ó Ö Õ P usw.

Die sort-Funktion sortiert diese langen Vokale hinter das Z, was der ANSI-Sortierung entspricht. :(

Wie kann man es nun bewerkstelligen, daß die Sortierreihenfolge genau der entspricht, die oben beschrieben habe?

Schöne und viele Grüße von
André :)

  1. Hi André, frisch aus dem Labor ---->

    =test it!

    sort by own list

    @xlist = qw(A A I Í B C D E Á É);

    @xay = sort { roro($a) <=> roro($b) } @xlist;

    for (@xay){print "$_\n"}
    ##########################################################################

    RoRo Translation Function, sort by own list :)

    sub roro{
     my $v = shift;
     my @ownlist = qw(A Á B C D E É F G H I Í); # legt die Reihenfolge fest...
     for( $i = 0; $i <= scalar (@ownlist); $i++ ){
      if ($v eq $ownlist[$i]){return $i}
     }
    }

    =eof
    ; Rolf

  2. Moin,

    [...]

    A Á B C D E É F G H I Í ... N O Ó Ö Õ P usw.
    Die sort-Funktion sortiert diese langen Vokale hinter das Z, was der ANSI-Sortierung entspricht. :(
    Wie kann man es nun bewerkstelligen, daß die Sortierreihenfolge genau der entspricht, die oben beschrieben habe?

    wenn der Rechner "ungarisch kann", sollte 'use locale;' das Erwuenschte bewirken.

    Aber ich persoenlich traue 'use locale' nicht ueber den Weg, das ist mir zu abhaengig vom System.

    Wie sind die Zeichen codiert? Sind die alle im ISO-8859-1 zu finden? Unicode?

    Mir fallen grundsaetzlich zwei Moeglichkeiten ein:

    1.)
    Man koennte sich ein Array bauen, in dem in der Wunschreihenfolge die Zeichen (in der entsprechenden Codierung, UTF-8 vielleicht) stehen.
    Die Vergleichsfunktion nimmt sich dann die Strings auseinander und vergleicht sie entsprechend dem Array.

    2.) 1.) duerfte sehr performancelastig sein, gerade bei groesseren zu sortierenden Listen.
    Man benutzt das in 1.) gebaute Array und baut sich einen Hash, der als Schluessel die zu sortierende Liste enthaelt und als Werte die Schluesselstrings mutiert [1]. Also so umgebaut, dass die ASCII-Vergleichsfunktion, die dann auf die Werte angewandt wird, das richtige Ergebnis liefert.
    Das klingt etwas komplizierter als 1.). Ist es auch (ein bisschen)[2], duerfte aber letztendlich bedeutend effektiver sein.

    [1] mir faellt gerade kein besseres Wort ein ;)

    [2] das ist die Grundidee, mit einer genauen Implementierung muesste ich mich noch beschaeftigen, das Thema interessiert mich jedenfalls ungemein ;)

    Viele Gruesse,

    n.d.p.

    1. moin n.d. :)

      Mir fallen grundsaetzlich zwei Moeglichkeiten ein:

      1.)
      Man koennte sich ein Array bauen, in dem in der Wunschreihenfolge die Zeichen (in der entsprechenden Codierung, UTF-8 vielleicht) stehen.
      Die Vergleichsfunktion nimmt sich dann die Strings auseinander und vergleicht sie entsprechend dem Array.

      genau das macht die RoRo Translation Function *g

      2.) 1.) duerfte sehr performancelastig sein, gerade bei groesseren zu sortierenden Listen.

      hmm , stimmt!

      Man benutzt das in 1.) gebaute Array und baut sich einen Hash, der als Schluessel die zu sortierende Liste enthaelt und als Werte die Schluesselstrings mutiert [1]. Also so umgebaut, dass die ASCII-Vergleichsfunktion, die dann auf die Werte angewandt wird, das richtige Ergebnis liefert.
      Das klingt etwas komplizierter als 1.). Ist es auch (ein bisschen)[2], duerfte aber letztendlich bedeutend effektiver sein.

      so isses, bin am Schreiben (110 Anschläge/min) hash, hash ... die Lösung liegt in einem Hasch - das geht fix!

      [2] das ist die Grundidee, mit einer genauen Implementierung muesste ich mich noch beschaeftigen, das Thema interessiert mich jedenfalls ungemein ;)

      Vom Prinzip habch den Kram ja schon fertig ;-)

      Rolf

    2. #!/usr/bin/perl

      sort by own list

      @xlist = qw(D D E É Í A A I Í B C D E Á É);

      @xay = sort { roro($a) <=> roro($b) } @xlist;

      for (@xay){print "$_\n"}
      ##########################################################################

      RoRo Translation Function, sort by own list :)

      sub roro{
       my $v = shift;
       my %ownlist = (
              "A",0,
       "Á",1,
       "B",2,
       "C",3,
       "D",4,
       "E",5,
       "É",6,
       "F",7,
       "G",8,
       "H",9,
       "I",10,
       "Í",11
       ); # legt die Reihenfolge fest...
       return $ownlist{$v};
      }

      1. Moin Rolf,

        sub roro{

        [...]

        ich habe eine halbe Stunde zum Posten gebraucht ;) und dein erstes Posting erst nach dem absenden gesehen ;-)

        Aber bei naeherem Hinsehen gibts da ein Problem: du sortierst einzelne Zeichen, bei ganzen Strings versagt das leider ;(
        ich fuerchte, es ist nicht ganz so einfach.

        Ich mach mal auch noch n Kopf und melde mich mal, wenn ich soweit bin ;) (ich muss gerade mal am Forum arbeiten...)
        André (Namensvetter ;), bei dir dann auch.

        Ich stelle mir ein allgemeines Modul vor, auf das man Sprachspezifiken aufsetzen kann (vorzugsweise das ganze UTF-8 encoded ;-)
        Also sowas wie 'Sort::Locale'. Dazu kann man dann 'Sort::Locale::Hungarian', 'Sort::Locale::German' usw. schreiben, welche die Reihenfolge vordefinieren. Im CPAN habe ich gerade das Modul No::Sort von Gisle Aas gefunden, dass norwegisch sortiert. Das benutzt noch einen anderen Ansatz (soweit ich das beim Ueberfliegen des Codes mitbekommen habe)

        Ich finde, das waere sogar was fuers CPAN ;-)

        Viele Gruesse,

        n.d.p.

        1. Moin Rolf,

          hi too! moin ;-)

          Aber bei naeherem Hinsehen gibts da ein Problem: du sortierst einzelne Zeichen, bei ganzen Strings versagt das leider ;(
          ich fuerchte, es ist nicht ganz so einfach.

          n.d. fürchte dich nicht :)

          &roro funktioniert auch mit Strings!
           see here:

          =cut
          #!/usr/bin/perl

          sort by own list

          @xlist = qw(DYZ DEE DYZ DAA DXL DDE DXL DAA);

          @xay = sort { roro($a) <=> roro($b) } @xlist;

          for (@xay){print "$_\n"}
          ##########################################################################

          RoRo Translation Function, sort by own list :)

          sub roro{
           my $v = shift;
           my %ownlist = (
            "DAA",0,
            "DDE",1,
            "DEE",2,
            "DXL",3,
            "DYZ",4
           );
           return $ownlist{$v};
          }
          =cut

          Ich stelle mir ein allgemeines Modul vor, auf das man Sprachspezifiken aufsetzen kann (vorzugsweise das ganze UTF-8 encoded ;-)
          Also sowas wie 'Sort::Locale'. Dazu kann man dann 'Sort::Locale::Hungarian', 'Sort::Locale::German' usw. schreiben, welche die Reihenfolge vordefinieren. Im CPAN habe ich gerade das Modul No::Sort von Gisle Aas gefunden, dass norwegisch sortiert. Das benutzt noch einen anderen Ansatz (soweit ich das beim Ueberfliegen des Codes mitbekommen habe)

          Ich finde, das waere sogar was fuers CPAN ;-)

          Cool !

          Herzliche Grüße aus Linkenheim (Baden) ; Rolf

          1. Hi Rolf,

            n.d. fürchte dich nicht :)

            &roro funktioniert auch mit Strings!
            see here:

            =cut
            #!/usr/bin/perl

            sort by own list

            @xlist = qw(DYZ DEE DYZ DAA DXL DDE DXL DAA);

            @xay = sort { roro($a) <=> roro($b) } @xlist;

            for (@xay){print "$_\n"}
            ##########################################################################

            RoRo Translation Function, sort by own list :)

            sub roro{
            my $v = shift;
            my %ownlist = (
              "DAA",0,
              "DDE",1,
              "DEE",2,
              "DXL",3,
              "DYZ",4
            );
            return $ownlist{$v};
            }
            =cut

            Das meinte Andy nicht ;-)
            Er meinte eher sowas wie

            =cut
            #!/usr/bin/perl

            sort by own list

            @xlist = qw(Áber Bisher Çlappts Doch);
            @xay = sort { roro($a) <=> roro($b) } @xlist;
            for (@xay){print "$_\n"}

            ##########################################################################

            RoRo Translation Function, sort by own list :)

            sub roro{
             my $v = shift;
             my %ownlist = (
              "À",0,
              "B",1,
              "Ç",2,
              "D",3
             );

            return $ownlist{$v};
            }
            =cut

            Das geht dann natuerlich nicht so. Bei der Liste muss solange jedes Zeichen der Strings verglichen werden, bis
            eindeutig fest steht, welcher String zuerst kommt.

            Gruss,
             Christian
             http://wwwtech.de

  3. Hi André,

    hier noch mal im Klartext meine Lösung für Dich:

    =cut
    #!/usr/bin/perl

    sort by ungarian Vocals

    @dirty_list = qw(A Ó P Á B C Ö D Õ E É B O Ö F G H I Í A A A);

    @sort_list = sort { roro($a) <=> roro($b) } @dirty_list;

    for (@sort_list){print "$_\n"}
    ####################################################################

    RoRo Translation Function, sort by own list :)

    sub roro{
     my $v = shift;
     my %ownlist = (
      "A",1,
      "Á",2,
      "B",3,
      "C",4,
      "D",5,
      "E",6,
      "É",7,
      "F",8,
      "G",9,
      "H",10,
      "I",11,
      "Í",12,
      "N",13,
      "O",14,
      "Ó",15,
      "Ö",16,
      "Õ",17,
      "P",18
     );
     return $ownlist{$v};
    }
    =cut

    Mit anderen Worten: Du musst dir die Liste der Sortier Reihenfolge einfach selbst schreiben - in den o.g. hash in der Subfunktion.

    Viele Grüße, Rolf

  4. Hallo,

    Wie wäre es mit folgendem:
    Ich vergleiche in der Sort-Funktion die ASCII-Werte der einzelenen Zeichen in den Strings.
    Für die Zeichen zwischen beispielsweise O und P ordne ich nun die Sonderzeichen mit Realzahlen ein.
    Der Witz an der Sache ist, daß Du im Hash nur jene Buchstaben einordnen mußt, welche wirklich irgenwo zwischengeschoben gehören.

    <code>
    my(%Special) = ('Á',ord('A')+0.1,
          'É',ord('E')+0.1,
          'Í',ord('I')+0.1,
          'Ó',ord('O')+0.1,
          'Ö',ord('O')+0.2,
          'Õ',ord('O')+0.3,
          #usw
          );

    @sortarray = ('AC','AA','AZ','AÁ');
    $, = "\n";
    print sort hungarian_sort @sortarray;

    sub hungarian_sort
    {
    return 0 if $a eq $b; # geht wahrscheinlich schneller
    my(@str_a) = split(//,$a);
    my(@str_b) = split(//,$b);
    my $i;

    for $i(0 .. $#str_a)
      {
      my($ca) = get_ord($str_a[$i]);
      my($cb) = get_ord($str_b[$i]);
      return ($ca <=> $cb) if($ca != $cb);
      }
    return 0;
    }

    sub get_ord
    {
    my($c) = shift;
    return $Special{$c}?$Special{$c}:ord($c);
    }
    </code>

    Naja ist vielleicht nicht gerade das, was bei einem Professor für Informatik Begeisterung hervorruft, aber es funktioniert soweit ich es getestet habe, und was die Theorie sagt ist dann sowieso schnuppe ;-)

    Grüße
      Klaus

  5. Hallo André,

    Vielleicht stelle ich mich auch nur blöd an (nachdem die anderen sich schon so viel Mühe gemacht haben, bin ich ein wenig verunsichert), aber für mich sieht das nach einer Aufgabe für eine Schwartz`sche Transformation aus (ich kann natürlich auch vollkommen falsch liegen :-)

    my %specchars = (
          'á' => 'a',
          'é' => 'e',
          'í' => 'i',
          'ó' => 'o',
          'ú' => 'u',
       );
    my $regex = join '|', keys %specchars;

    my @sorted = map  { $_->[1] }
                 sort { $a->[0] cmp $b->[0] }
                 map  { ( my $r = $_ ) =~ s/($regex)/$specchars{$1}/og;
                        [ lc( $r ), $_ ];
                      } @asorted_list_with_funny_characters;

    Vielleicht ist das auch nicht sonderlich performant, aber es dürfte den von André Malo geforderten Bedingungen doch recht nahe kommen (Zwei Listen bzw. Schluessel-Wert-Paare, wo a) unbearbeitet, und b) nach Ascii transformierte Zeichen enthält)
    Zumindest erspart es viele Funktionsaufrufe in sort(), wobei natürlich die Liste auf jeden Fall mindestens zweimal mit map() durchlaufen werden muss.

    Gruß AlexBausW, der sich freut, auch einen Beitrag geleistet zu haben :-))

  6. Hallo, Ihr wertvollen Helfer, am Pfingstsonntag,

    Danke schön für die vielen Tips, die ich fast alle ausprobiert habe.

    Rolf's Variante funktioniert prima, aber nur für einzelne Zeichen, d.h. wenn man die zu sortierende Liste in

    @dirty_list = qw(APÓ ÓPA PAA ÁBB BCP CÖD ÖÖÖ DDD ÕPB E É B O Ö F G H I Í A A A);

    umwandelt, klappt es nicht mehr. Nur die hinteren Characters werden sortiert.

    Klaus' Vorschlag ist sehr elegant, funktioniert auch, ist aber ziemlich langsam. Zudem hatte er meinen Speicher zugemacht.

    Alex' Version habe ich erst gerade gelesen, nachdem ich mich in den frühen Morgenstunden für ein Modul entschieden hatte, das ich unter CPAN fand.

    Die Adresse lautet: http://search.cpan.org/search?dist=Sort-ArbBiLex

    Es läuft auf meiner lokalen Maschine unter NT sehr gut.

    Prinzip:

    use Sort::ArbBiLex ( 'fulani_sort',
       [
         [ "a", "A" ],
         [ "c", "C" ],
         [ "ch", "Ch", "CH" ],
         [ "ch'", "Ch'", "CH'" ],
         [ "e", "E" ],
         [ "l", "L" ],
         [ "lh", "Lh", "LH" ],
         [ "n", "N" ],
         [ "r", "R" ],
         [ "s", "S" ],
         [ "u", "U" ],
         [ "z", "Z" ],
       ]
      );

    Es müssen also alle zu sortierenden Zeichen angegeben werden. Zeichenverbindungen, wie im Ungarischen üblich (Cs, Gy, Ly, Ny usw.), können auch als Sortierkriterium herangezogen werden, lassen die Performance aber etwas schlechter werden. Das Modul hilft aber gut wirtschaften.

    Nochmals recht herzlichen Dank an Euch, und weiterhin ein schönes Pfingstfest. :)

    André