Oppolzer - Informatik / Blog


Blog-Hauptseite      Neuester Artikel      Älterer Artikel      Neuerer Artikel      Älterer gleiche Kategorie      Neuerer gleiche Kategorie

Shareware - Quicksort in REXX

Subject:

Quicksort in REXX und Probleme damit

From:

Bernd Oppolzer <bernd.oppolzer@T-ONLINE.DE>

Reply-To:

Date:

2015.01.15 15:10:00


Für die Bereitstellung der Artikel dieses Blogs war es nötig, die Artikel
sowie die Links auf der Blog-Hauptseite nach Zeitangaben zu sortieren. Die
statischen HTML-Seiten werden mit einem REXX-Skript erstellt.

Es geht also darum, einen REXX-Stem zu sortieren, idealerweise mit dem
allseits bekannten Quicksort-Verfahren. Das Problem ist, dass Quicksort
rekursive Funktionsaufrufe benutzt.

Eine an die von der Wikipedia-Seite zu Quicksort abgeleitete Formulierung
für REXX sieht wie folgt aus:


/************************************************/
/*                                              */
/*   Sortieren der art-Stems nach dem           */
/*   Datum bzw. Filenamen                       */
/*                                              */
/*   Quicksort in REXX                          */
/*                                              */
/************************************************/

sort:

   daten. = ""
   daten.0 = art.0

   do i = 1 to daten.0
      daten.i = artf.i" $ "i
   end

   /* call quicksort 1, daten.0 */

   call quicksort_nonrec

   do i = 1 to daten.0
      say daten.i
   end

   return



/***********************************************/
/*                                             */
/*   quicksort                                 */
/*                                             */
/***********************************************/

quicksort: procedure expose daten.

   parse arg links, rechts

   say "quicksort startet mit "links","rechts

   if links < rechts then do
      teiler = teile(links, rechts)
      call quicksort links, teiler - 1
      call quicksort teiler + 1, rechts
   end

   return


dazu folgende Bemerkungen:

a) der Quicksort-Aufruf in der Funktion sort ist auskommentiert, weil sich
   schnell herausgestellt hat, dass das so nicht funktioniert (die Anzahl
   der rekursiven Aufrufebenen war bei meiner REXX-Implementierung - OS/2 V3.0 -
   auf ca. 45 Ebenen begrenzt ... das reicht bei weitem nicht aus)

b) die Funktion sort zeigt auf, wie eine ganze Reihe von Stems wie z.B. der
   Stem artf sortiert werden kann; sie werden einfach gar nicht sortiert,
   sondern es wird einfach eine Key-Tabelle aufgebaut (hier: Daten), und in
   dieser steht der Schlüsselbegriff für die Sortierung (hier: artf) und
   der ursprüngliche Index. Diese beiden werden sortiert, so dass man nachher
   den Zugriff auf die Tabellen anhand des ursprünglichen Index vornehmen kann
   (indirekter Zugriff über den umsortierten Index)

c) die eigentliche Arbeit der Vertauschung usw. geschieht in der Funktion
   teile; sie kommt ebenfalls mehr oder weniger direkt von Wikipedia; hier
   die REXX-Formulierung:


/***********************************************/
/*                                             */
/*   teilen des Vektors fuer quicksort         */
/*                                             */
/***********************************************/

teile: procedure expose daten.

   parse arg li, re

   piv = daten.re

   /***************************************/
   /* starte mit j links vom pivotelement */
   /***************************************/

   i = li
   j = re - 1;

   do while i < j

      /*********************************************/
      /*   suche von links ein element,            */
      /*   welches groesser als das pivot          */
      /*   wiederhole solange daten[i] <= pivot    */
      /*   und i < rechts                          */
      /*********************************************/

      do while daten.i >= piv & i < re
         i = i + 1
      end

      /*********************************************/
      /*   suche von rechts ein element,           */
      /*   welches kleiner als das pivot           */
      /*   wiederhole solange daten[j] >= pivot    */
      /*   und j > links                           */
      /*********************************************/

      do while daten.j <= piv & j > li
         j = j - 1
      end;

      /*********************************************/
      /*   falls i < j dann                        */
      /*   tausche daten[i] mit daten[j]           */
      /*********************************************/

      if i < j then do

         daten_save = daten.i
         daten.i = daten.j
         daten.j = daten_save

      end

   end

   if daten.i < piv then do

      /*********************************************/
      /*   tausche daten[i] mit daten[rechts]      */
      /*********************************************/

      daten_save = daten.i
      daten.i = daten.re
      daten.re = daten_save

   end

   return i


Jetzt zum Problem mit den rekursiven Aufrufen. Die Lösung ist hier,
dass man die Rekursion durch Iteration ersetzt. Ich habe einen weiteren
Stem call_stack eingeführt, der im wesentlichen die Parameter für die
rekursiven Aufrufe enthält. Die in der Folge eines Aufrufs von quicksort
als nächstes durchzuführenden "rekursiven" Aufrufe auf den unteren Ebenen
werden in den call_stack eingetragen (quasi für später beauftragt). Die
Variable stack_pos steuert, wo man sich innerhalb des call_stacks gerade
befindet.

Die nicht-rekursive Variante von Quicksort sieht damit wie folgt aus:


/***********************************************/
/*                                             */
/*   quicksort nicht rekursiv                  */
/*                                             */
/***********************************************/

quicksort_nonrec: procedure expose daten.

   call_stack. = ""
   call_stack.1 = 1" "daten.0
   stackpos = 1

   do while stackpos > 0

      links = word(call_stack.stackpos, 1)
      rechts = word(call_stack.stackpos, 2)

      say "quicksort_nonrec ("stackpos") läuft mit "links","rechts

      if links < rechts then do

         teiler = teile(links, rechts)

         li_neu = teiler + 1
         re_neu = rechts
         call_stack.stackpos = li_neu" "re_neu

         stackpos = stackpos + 1

         li_neu = links
         re_neu = teiler - 1
         call_stack.stackpos = li_neu" "re_neu

      end
      else do
         stackpos = stackpos - 1
      end

   end

   return


Damit gab's dann kein Problem mehr; die Information, die vorher in
den call_stack abgelegt wurde (Problem schon bei 60 Elementen), liegt
jetzt im Stem call_stack, und da ist genügend Platz ... auch bei meinem
quasi historischen OS/2-System.

Blog-Hauptseite      Neuester Artikel      Älterer Artikel      Neuerer Artikel      Älterer gleiche Kategorie      Neuerer gleiche Kategorie