next up previous
Next: Synchronisierungsmechanismen Up: Koroutinen Previous: Das Ulmer Koroutinenkonzept

Iteratoren

Im obigen Beispiel erfolgt der Wechsel in der Kontrolle explizit. Damit wird der Programmtext schwerer lesbar und auch abhängig von der verwendeten Technik der Parallelisierung. Das Modul Iterators in der Ulmer Oberon-Bibliothek erlaubt die Realisierung von Produzenten-Konsumenten-Problemen unabhängig von der Art des darunterliegenden Koroutinenschemas.

Bei Iteratoren wird nur eine Koroutine zusätzlich kreiert, die die Rolle des Produzenten übernimmt. Danach übernimmt die aufrufende Koroutine die Rolle des Konsumenten. Verbunden sind beide über die Abstraktion eines Iterators: Der Produzent liefert ein produziertes Objekt mit Yield, während der Konsument sie mit Get abholt:

TYPE Reference = Objects.Object; Mode = SHORTINT;
TYPE Iterator = POINTER TO IteratorRec;
TYPE IteratorRec = RECORD (Services.ObjectRec) END;
TYPE IteratorProc = PROCEDURE (it: Iterator; ref: Reference; mode: Mode);

PROCEDURE Create(VAR it: Iterator; itproc: IteratorProc;
                 ref: Reference; mode: Mode);
PROCEDURE Yield(it: Iterator; object: Objects.Object);
PROCEDURE Get(it: Iterator; VAR object: Objects.Object) : BOOLEAN;

Natürlich muß der Prozedurtyp einer Koroutinenprozedur festgelegt werden, wenn man wie bei Iterators eine Abstraktion zur Parallelisierung unabhängig von der direkten Verwendung von CRSPAWN und CRSWITCH gestalten möchte. Allerdings ist das nicht so unangenehm wie in Modula-2, da über Typerweiterungen letztendlich beliebig parametrisiert werden kann. Der Prozedurtyp IteratorProc sieht natürlich als Parameter den Verweis auf den Iterator selbst vor, dann eine beliebige Erweiterung von Objects.Object und einen weiteren kleinen Parameter, der zur Auswahl verschiedener Iterationsmodi dienen kann (z.B. aufsteigend oder absteigend bei sortierten Listen).

Im Gegensatz zu direkten Koroutinenprozeduren darf die bei Create übergebene Prozedur die Iteration mit RETURN beenden. Das führt dann dazu, daß Get auf der Konsumentenseite FALSE zurückliefert.

Es lohnt sich, Iteratoren bei zahlreichen Abstraktionen wie z.B. bei Collections zum Durchwandern von Datenstrukturen einzusetzen, weil dadurch

Entsprechend wäre es sinnvoll, bei Collections statt First und Next eine Operation GetIterator zur Verfügung zu stellen:[*]

PROCEDURE GetIterator(collection: Collection;
                      VAR iterator: Iterators.Iterator);

Bei der zugehörigen Implementierung Lists wird der Iterator durch die Prozedur IterateList repräsentiert, die dann durch die Schnittstellenprozedur GetIterator zum Leben erweckt wird:

PROCEDURE IterateList(it: Iterators.Iterator;
                      list: Objects.Object;
                      param: SHORTINT); (* ignored *)
   VAR
      linkable: Linkable;
BEGIN
   WITH list: List DO
      linkable := list.head;
      WHILE linkable # NIL DO
         Iterators.Yield(it, linkable.object);
         linkable := linkable.next;
      END;
   END;
END IterateList;

PROCEDURE GetIterator(collection: Collections.Collection;
                      VAR iterator: Iterators.Iterator);
BEGIN
   Iterators.Create(iterator, IterateList, collection, 0);
END GetIterator;

Der vierte Parameter von Iterators.Create wird als dritter Parameter an IterateList übergeben und dient zur vereinfachten Parameterisierung des Iterators, so kann beispielsweise die Reihenfolge spezifiziert werden. In diesem Beispiel wird davon aber kein Gebrauch gemacht.

In PrintableCollections könnte dann ein Iterator auf folgende Weise zum Durchlaufen einer Kollektion verwendet werden:

PROCEDURE PrintCollection(s: Streams.Stream; object: Services.Object);
   VAR
      member: Disciplines.Object;
      first: BOOLEAN;
      it: Iterators.Iterator;
BEGIN
   WITH object: Collections.Collection DO
      Write.CharS(s, "(");
      Collections.GetIterator(object, it);
      first := TRUE;
      WHILE Iterators.Get(it, member) DO
         IF first THEN
            first := FALSE;
         ELSE
            Write.StringS(s, ", ");
         END;
         IF member IS Services.Object THEN
            PrintableObjects.Print(s, member(Services.Object));
         ELSE
            Write.CharS(s, "?");
         END;
      END;
      Write.StringS(s, ")");
   END;
END PrintCollection;


next up previous
Next: Synchronisierungsmechanismen Up: Koroutinen Previous: Das Ulmer Koroutinenkonzept
Andreas Borchert
2/2/1998