Zum Inhalt springen

„Sentinel (Programmierung)“ – Versionsunterschied

aus Wikipedia, der freien Enzyklopädie
[gesichtete Version][gesichtete Version]
Inhalt gelöscht Inhalt hinzugefügt
K Beispiel: leichte Vereinheitlichung
Zeile 11: Zeile 11:
{
{
int key;
int key;
struct sll_node *next; // end-of-list-Indikator oder -> nächster Knoten
struct sll_node *next; // -> nächster Knoten oder Terminator der Liste
} sll,
} sll,
*first; // globale Variable
*first; // globale Variable
</syntaxhighlight>
</syntaxhighlight>
nach einem Schlüsselwert <code>search_key</code> gesucht werden – bei gleichem Suchergebnis.
nach einem Schlüsselwert <code>search_key</code> gesucht werden – bei gleichem Suchergebnis.
Zeile 22: Zeile 22:
// Global:
// Global:
first = NULL; // Terminierung vor der ersten Einfügung
first = NULL; // Terminierung vor der ersten Einfügung
// NB: Einfügung nicht gebracht. Sie hat aber
// NB: Einfügung nicht gebracht. Sie hat aber für ...
// für den immer gleichen Terminator (hier: Nullzeiger) zu sorgen.
// den immer gleichen Terminator (hier: Nullzeiger) zu sorgen.


struct sll_node *Search(int search_key)
struct sll_node *Search(int search_key)
Zeile 38: Zeile 38:
}
}
</syntaxhighlight>
</syntaxhighlight>
Die <code>for</code>-Schleife enthält pro Schleifenschritt die zwei Abfragen
Die <code>for</code>-Schleife enthält pro Schleifenschritt die zwei (gelb hinterlegte) Abfragen
# <code>if (node != NULL)</code> und
* <code>if (node != NULL)</code> und
# <code>if (node->key == search_key)</code> (beide gelb hinterlegt).
* <code>if (node->key == search_key)</code>.


=== Version mit Sentinel ===
=== Version mit Sentinel ===
Zeile 50: Zeile 50:
sentinel->next = sentinel;
sentinel->next = sentinel;
first = sentinel; // Terminierung vor der ersten Einfügung
first = sentinel; // Terminierung vor der ersten Einfügung
// NB: Einfügung nicht gebracht. Sie hat aber
// NB: Einfügung nicht gebracht. Sie hat aber für ...
// für den immer gleichen Terminator (hier: Zeigerwert) zu sorgen.
// den immer gleichen Terminator (hier: Zeigerwert) zu sorgen.


struct sll_node *SearchWithSentinel(int search_key)
struct sll_node *SearchWithSentinel(int search_key)
Zeile 61: Zeile 61:
for (node = first;
for (node = first;
node->key != search_key;
node->key != search_key;
node = node->next) {}
node = node->next)
{
}
if (node != sentinel)
if (node != sentinel)
return node; // gefunden
return node; // gefunden
Zeile 67: Zeile 69:
}
}
</syntaxhighlight>
</syntaxhighlight>
Die <code>for</code>-Schleife enthält pro Schleifenschritt nur die eine Abfrage
Die <code>for</code>-Schleife enthält pro Schleifenschritt nur die eine (gelb hinterlegte) Abfrage
#<code>if (node->key != search_key)</code> (gelb hinterlegt).
* <code>if (node->key != search_key)</code>.
;Bemerkung
;Bemerkung
Wird auf die Datenstruktur [[Parallele Programmierung|parallel]] (konkurrent) zugegriffen, dann gehört auch <code>SearchWithSentinel</code> (modifizierende Operationen sowieso) in einen [[Kritischer Abschnitt|kritischen Abschnitt]], der durch ein [[Mutex]] abgesichert werden muss.
Wird auf die Datenstruktur [[Parallele Programmierung|parallel]] (konkurrent) zugegriffen, dann gehört auch das Suchen per <code>SearchWithSentinel</code> (modifizierende Operationen wie Einfügen und Löschen sowieso) in einen [[Kritischer Abschnitt|kritischen Abschnitt]], der durch ein [[Mutex]] abgesichert werden muss. Dieser Nachteil kann wesentlich schwerer wiegen als der marginale Vorteil durch das Einsparen einer Abfrage pro Schleifenschritt.


== Siehe auch ==
== Siehe auch ==

Version vom 20. Oktober 2019, 17:22 Uhr

Als Sentinel (Aussprache: sentinel/?, engl. für Wächter), Wächter oder Wächterwert (im engeren Sinn) bezeichnet man in der Informatik, im Bereich der Programmierung, ein Konstrukt, welches eine Sequenz derart terminiert, dass die Programmlogik nach einer erfolglosen Inspektion aller echten Fälle abschließend (mit unechtem Erfolg) auf das Ergebnis »gefunden« läuft.[1] Wenn so geschehen, wird nachträglich das Ergebnis auf »nicht gefunden« korrigiert.

Mit diesem Trick wird die Anzahl der Abfragen innerhalb der Suchschleife um eine, nämlich die Abfrage auf das Ende der Sequenz, verringert – auf Kosten geringfügig komplizierterer Erfordernisse und Aktionen außerhalb der Schleife.

Im weiteren Sinn gilt (insbesondere im englischen Sprachraum) jede Form eines Terminators einer Sequenz, so auch das Nullzeichen bei Zeichenketten, als Sentinel.

Beispiel

In den beiden folgenden C-Funktionen Search und SearchWithSentinel soll in einer verketteten Liste vom Typ

struct sll_node               // ein Knoten der verketteten Liste sll
{
   int key;
   struct sll_node *next;     // -> nächster Knoten oder Terminator der Liste
} sll,
*first;                       // globale Variable

nach einem Schlüsselwert search_key gesucht werden – bei gleichem Suchergebnis.

Version mit NULL

Die Liste sll wird terminiert durch den Nullwert NULL.

// Global:
first = NULL;                 // Terminierung vor der ersten Einfügung
// NB: Einfügung nicht gebracht. Sie hat aber für ...
//     den immer gleichen Terminator (hier: Nullzeiger) zu sorgen.

struct sll_node *Search(int search_key)
{
    struct sll_node *node;
    for (node = first;
         node != NULL;
         node = node->next)
    {
        if (node->key == search_key)
            return node;      // gefunden
    }
    return NULL;              // nicht gefunden
}

Die for-Schleife enthält pro Schleifenschritt die zwei (gelb hinterlegte) Abfragen

  • if (node != NULL) und
  • if (node->key == search_key).

Version mit Sentinel

Der global zugreifbare Zeiger sentinel zum für die Schleife gezielt zu präparierenden Objekt Sentinel dient als Terminator der Liste sll.

// Globale Variable:
struct sll_node Sentinel, *sentinel = &Sentinel;
// Global:
sentinel->next = sentinel;
first = sentinel;             // Terminierung vor der ersten Einfügung
// NB: Einfügung nicht gebracht. Sie hat aber für ...
//     den immer gleichen Terminator (hier: Zeigerwert) zu sorgen.

struct sll_node *SearchWithSentinel(int search_key)
{
    struct sll_node *node;
    // gezielte Präparation:
    sentinel->key = search_key;

    for (node = first;
         node->key != search_key;
         node = node->next)
    {
    }
    if (node != sentinel)
        return node;          // gefunden
    return NULL;              // nicht gefunden
}

Die for-Schleife enthält pro Schleifenschritt nur die eine (gelb hinterlegte) Abfrage

  • if (node->key != search_key).
Bemerkung

Wird auf die Datenstruktur parallel (konkurrent) zugegriffen, dann gehört auch das Suchen per SearchWithSentinel (modifizierende Operationen wie Einfügen und Löschen sowieso) in einen kritischen Abschnitt, der durch ein Mutex abgesichert werden muss. Dieser Nachteil kann wesentlich schwerer wiegen als der marginale Vorteil durch das Einsparen einer Abfrage pro Schleifenschritt.

Siehe auch

Einzelnachweise

  1. Kurt Mehlhorn, Peter Sanders: Algorithms and Data Structures. The Basic Toolbox. Springer, Berlin / Heidelberg 2008, ISBN 978-3-540-77977-3, S. 63, doi:10.1007/978-3-540-77978-0. 3 Representing Sequences by Arrays and Linked Lists