Generische Programmierung – Wikipedia

before-content-x4

Methode zum Entwerfen und Schreiben von Programmen, bei denen Algorithmen in Form von Parametertypen geschrieben werden, die eine einfache Wiederverwendung ermöglichen

Generische Programmierung ist eine Art der Computerprogrammierung, bei der Algorithmen in Form von Typen geschrieben werden später spezifiziert werden das sind dann instanziiert bei Bedarf für bestimmte Typen, die als Parameter bereitgestellt werden. Dieser Ansatz, der 1973 von der Programmiersprache ML entwickelt wurde,[1][2] Ermöglicht das Schreiben allgemeiner Funktionen oder Typen, die sich nur in der Gruppe der Typen unterscheiden, mit denen sie bei Verwendung arbeiten, wodurch Doppelarbeit verringert wird. Solche Software-Entitäten sind bekannt als Generika in Python, Ada, C #, Delphi, Eiffel, F #, Java, Nim, Rust, Swift, TypeScript und Visual Basic .NET. Sie sind bekannt als parametrischer Polymorphismus in ML, Scala, Julia und Haskell (die Haskell-Community verwendet den Begriff “generisch” auch für ein verwandtes, aber etwas anderes Konzept); Vorlagen in C ++ und D; und parametrisierte Typen in dem einflussreichen Buch von 1994 Designmuster.[3]

Der Begriff “generische Programmierung” wurde ursprünglich von David Musser und Alexander Stepanov in einem spezifischeren Sinne als dem oben genannten geprägt, um ein Programmierparadigma zu beschreiben, bei dem grundlegende Anforderungen an Typen aus konkreten Beispielen von Algorithmen und Datenstrukturen abstrahiert und als Konzepte formalisiert werden. mit generischen Funktionen, die im Hinblick auf diese Konzepte implementiert sind, typischerweise unter Verwendung von Sprachgenerierungsmechanismen, wie oben beschrieben.

Stepanov-Musser und andere generische Programmierparadigmen[edit]

Die generische Programmierung ist in Musser & Stepanov (1989) wie folgt definiert:

Die generische Programmierung konzentriert sich auf die Idee, von konkreten, effizienten Algorithmen zu abstrahieren, um generische Algorithmen zu erhalten, die mit verschiedenen Datendarstellungen kombiniert werden können, um eine Vielzahl nützlicher Software zu erstellen.

– –Musser, David R.; Stepanov, Alexander A., ​​Generische Programmierung[5]

Das Paradigma der “generischen Programmierung” ist ein Ansatz zur Software-Zerlegung, bei dem grundlegende Anforderungen an Typen aus konkreten Beispielen von Algorithmen und Datenstrukturen abstrahiert und analog zur Abstraktion algebraischer Theorien in der abstrakten Algebra als Konzepte formalisiert werden.[6] Frühe Beispiele für diesen Programmieransatz wurden in Scheme und Ada implementiert.[7] Obwohl das bekannteste Beispiel die Standard Template Library (STL) ist,[8][9] das eine Theorie der Iteratoren entwickelte, die verwendet wird, um Sequenzdatenstrukturen und die darauf arbeitenden Algorithmen zu entkoppeln.

Zum Beispiel gegeben N. Sequenzdatenstrukturen, z. B. einfach verknüpfte Liste, Vektor usw., und M. Algorithmen, um sie zu bearbeiten, z finden, Sortieren usw. würde ein direkter Ansatz jeden Algorithmus spezifisch für jede Datenstruktur implementieren und geben N. × M. zu implementierende Kombinationen. Beim generischen Programmieransatz gibt jedoch jede Datenstruktur ein Modell eines Iteratorkonzepts zurück (ein einfacher Werttyp, der zum Abrufen des aktuellen Werts dereferenziert oder so geändert werden kann, dass er auf einen anderen Wert in der Sequenz verweist), und stattdessen wird jeder Algorithmus geschrieben generisch mit Argumenten solcher Iteratoren, z. B. einem Paar von Iteratoren, die auf den Anfang und das Ende der Teilsequenz zeigen, oder Reichweite herstellen. Also nur N. + M. Datenstruktur-Algorithmus-Kombinationen müssen implementiert werden. In der STL sind mehrere Iteratorkonzepte angegeben, von denen jedes eine Verfeinerung restriktiverer Konzepte darstellt, z. B. Vorwärtsiteratoren, die nur zum nächsten Wert in einer Sequenz führen (z. B. geeignet für eine einfach verknüpfte Liste oder einen Strom von Eingabedaten), während ein Direktzugriff möglich ist Der Iterator bietet auch direkten zeitkonstanten Zugriff auf jedes Element der Sequenz (z. B. geeignet für einen Vektor). Ein wichtiger Punkt ist, dass eine Datenstruktur ein Modell des allgemeinsten Konzepts zurückgibt, das effizient implementiert werden kann. Anforderungen an die Komplexität der Berechnungen sind explizit Teil der Konzeptdefinition. Dies begrenzt die Datenstrukturen, auf die ein bestimmter Algorithmus angewendet werden kann, und solche Komplexitätsanforderungen sind eine Hauptdeterminante für die Wahl der Datenstruktur. Die generische Programmierung wurde in ähnlicher Weise in anderen Bereichen angewendet, z. B. in Graph-Algorithmen.[10]

Beachten Sie, dass dieser Ansatz zwar häufig Sprachfunktionen für Generizität / Vorlagen zur Kompilierungszeit verwendet, jedoch tatsächlich unabhängig von bestimmten sprachtechnischen Details ist. Generischer Programmierpionier Alexander Stepanov schrieb:

Bei der generischen Programmierung geht es darum, Algorithmen und Datenstrukturen zu abstrahieren und zu klassifizieren. Es wird von Knuth und nicht von der Typentheorie inspiriert. Ihr Ziel ist die schrittweise Erstellung systematischer Kataloge nützlicher, effizienter und abstrakter Algorithmen und Datenstrukturen. Ein solches Unterfangen ist immer noch ein Traum.

– –Alexander Stepanov, Kurze Geschichte von STL [11][12]

Ich glaube, dass Iteratortheorien für die Informatik ebenso zentral sind wie Ringtheorien oder Banachräume für die Mathematik.

– –Alexander Stepanov, Ein Interview mit A. Stepanov[13]

Bjarne Stroustrup bemerkte,

Nach Stepanov können wir generische Programmierung definieren, ohne Sprachmerkmale zu erwähnen: Heben Sie Algorithmen und Datenstrukturen von konkreten Beispielen in ihre allgemeinste und abstrakteste Form.

– –Bjarne Stroustrup, Entwicklung einer Sprache in und für die reale Welt: C ++ 1991-2006[12]

Andere Programmierparadigmen, die als generische Programmierung beschrieben wurden, umfassen Generische Programmierung des Datentyps wie unter “Generische Programmierung – eine Einführung” beschrieben.[14] Das Verschrotte deine Boilerplate Ansatz ist ein leichter generischer Programmieransatz für Haskell.[15]

In diesem Artikel unterscheiden wir die übergeordneten Programmierparadigmen von generische Programmierungoben aus der untergeordneten Programmiersprache Generizitätsmechanismen wird verwendet, um sie zu implementieren (siehe Unterstützung für Programmiersprachen für allgemeine Informationen). Weitere Informationen und Vergleiche allgemeiner Programmierparadigmen finden Sie unter.[16]

Programmiersprachenunterstützung für Generizität[edit]

Genericity-Einrichtungen existieren seit mindestens den 1970er Jahren in Hochsprachen in Sprachen wie ML, CLU und Ada und wurden anschließend von vielen objektbasierten und objektorientierten Sprachen übernommen, darunter BETA, C ++, D, Eiffel, Java, und DECs inzwischen nicht mehr existierende Trellis-Owl-Sprache.

Genericity wird in verschiedenen Programmiersprachen unterschiedlich implementiert und unterstützt. Der Begriff “generisch” wurde auch in verschiedenen Programmierkontexten unterschiedlich verwendet. In Forth kann der Compiler beispielsweise Code beim Kompilieren ausführen und neuen erstellen Compiler-Schlüsselwörter und neue Implementierungen für diese Wörter im laufenden Betrieb. Es hat wenige Wörter das macht das Compilerverhalten sichtbar und bietet daher natürlich Großzügigkeit Kapazitäten, die in den meisten Forth-Texten jedoch nicht als solche bezeichnet werden. In ähnlicher Weise bieten dynamisch typisierte Sprachen, insbesondere interpretierte, normalerweise Großzügigkeit Standardmäßig sind sowohl die Übergabe von Werten an Funktionen als auch die Wertzuweisung typindifferent, und ein solches Verhalten wird häufig zur Abstraktion oder Codeverknappung verwendet. Dies wird jedoch normalerweise nicht gekennzeichnet Großzügigkeit da es eine direkte Folge des von der Sprache verwendeten dynamischen Typisierungssystems ist.[citation needed] Der Begriff wurde in der funktionalen Programmierung verwendet, insbesondere in Haskell-ähnlichen Sprachen, die ein strukturelles Typsystem verwenden, bei dem Typen immer parametrisch sind und der tatsächliche Code für diese Typen generisch ist. Diese Verwendungen dienen immer noch einem ähnlichen Zweck des Speicherns von Code und des Renderns einer Abstraktion.

Arrays und Strukturen können als vordefinierte generische Typen angesehen werden. Jede Verwendung eines Array- oder Strukturtyps instanziiert einen neuen konkreten Typ oder verwendet einen zuvor instanziierten Typ wieder. Array-Elementtypen und Strukturelementtypen sind parametrisierte Typen, mit denen der entsprechende generische Typ instanziiert wird. All dies ist normalerweise im Compiler integriert und die Syntax unterscheidet sich von anderen generischen Konstrukten. Einige erweiterbare Programmiersprachen versuchen, integrierte und benutzerdefinierte generische Typen zu vereinheitlichen.

Es folgt eine umfassende Übersicht über Generizitätsmechanismen in Programmiersprachen. Für eine spezifische Umfrage zum Vergleich der Eignung von Mechanismen für die generische Programmierung siehe.[17]

In objektorientierten Sprachen[edit]

Beim Erstellen von Containerklassen in statisch typisierten Sprachen ist es unpraktisch, spezifische Implementierungen für jeden enthaltenen Datentyp zu schreiben, insbesondere wenn der Code für jeden Datentyp praktisch identisch ist. In C ++ kann diese Duplizierung von Code beispielsweise umgangen werden, indem eine Klassenvorlage definiert wird:

template <typename T>
class List {
  // Class contents.
};

List<Animal> list_of_animals;
List<Car> list_of_cars;

Über, T. ist ein Platzhalter für den Typ, der beim Erstellen der Liste angegeben wird. Diese “Container vom Typ T”, allgemein als Vorlagen bezeichnet, ermöglichen die Wiederverwendung einer Klasse mit verschiedenen Datentypen, solange bestimmte Verträge wie Untertypen und Signaturen beibehalten werden. Dieser Generizitätsmechanismus sollte nicht mit verwechselt werden EinschlusspolymorphismusDies ist die algorithmische Verwendung austauschbarer Unterklassen: zum Beispiel eine Liste von Objekten vom Typ Moving_Object Objekte vom Typ enthalten Tier und Wagen. Vorlagen können auch für typunabhängige Funktionen wie in der verwendet werden Tauschen Beispiel unten:

// "&" passes parameters by reference
template <typename T>
void Swap(T& a, T& b) {
  T temp = b;
  b = a;
  a = temp;
}

std::string hello = "World!";
std::string world = "Hello, ";
Swap(world, hello);
std::cout << hello << world << std::endl;  // Output is "Hello, World!".

Das C ++ Vorlage Das oben verwendete Konstrukt wird häufig zitiert[citation needed] als das Generizitätskonstrukt, das den Begriff unter Programmierern und Sprachdesignern populär machte und viele generische Programmiersprachen unterstützt. Die Programmiersprache D bietet auch vollständig generische Vorlagen, die auf dem C ++ – Präzedenzfall basieren, jedoch eine vereinfachte Syntax aufweisen. Die Programmiersprache Java bietet seit Einführung von J2SE 5.0 syntaktische Funktionen, die syntaktisch auf C ++ basieren.

C # 2.0, Oxygene 1.5 (auch als Chrome bekannt) und Visual Basic .NET 2005 verfügen über Konstrukte, die die Unterstützung für Generika nutzen, die in Microsoft .NET Framework seit Version 2.0 vorhanden sind.

Generika in Ada[edit]

Ada hat Generika seit seiner ersten Entwicklung in den Jahren 1977-1980. Die Standardbibliothek verwendet Generika, um viele Dienste bereitzustellen. Ada 2005 fügt der Standardbibliothek eine umfassende generische Containerbibliothek hinzu, die von der Standardvorlagenbibliothek von C ++ inspiriert wurde.

EIN generische Einheit ist ein Paket oder ein Unterprogramm, das eines oder mehrere benötigt generische formale Parameter.

EIN generischer formaler Parameter ist ein Wert, eine Variable, eine Konstante, ein Typ, ein Unterprogramm oder sogar eine Instanz einer anderen, bezeichneten generischen Einheit. Bei generischen formalen Typen unterscheidet die Syntax zwischen diskreten, Gleitkomma-, Festkomma-, Zugriffs- (Zeiger-) Typen usw. Einige formale Parameter können Standardwerte haben.

Zu instanziieren eine generische Einheit, die der Programmierer übergibt tatsächlich Parameter für jede formale. Die generische Instanz verhält sich dann wie jede andere Einheit. Es ist möglich, generische Einheiten zur Laufzeit zu instanziieren, beispielsweise innerhalb einer Schleife.

Beispiel[edit]

Die Spezifikation eines generischen Pakets:

 generic
    Max_Size : Natural; -- a generic formal value
    type Element_Type is private; -- a generic formal type; accepts any nonlimited type
 package Stacks is
    type Size_Type is range 0 .. Max_Size;
    type Stack is limited private;
    procedure Create (S : out Stack;
                      Initial_Size : in Size_Type := Max_Size);
    procedure Push (Into : in out Stack; Element : in Element_Type);
    procedure Pop (From : in out Stack; Element : out Element_Type);
    Overflow : exception;
    Underflow : exception;
 private
    subtype Index_Type is Size_Type range 1 .. Max_Size;
    type Vector is array (Index_Type range <>) of Element_Type;
    type Stack (Allocated_Size : Size_Type := 0) is record
       Top : Index_Type;
       Storage : Vector (1 .. Allocated_Size);
    end record;
 end Stacks;

Instanziieren des generischen Pakets:

 type Bookmark_Type is new Natural;
 -- records a location in the text document we are editing

 package Bookmark_Stacks is new Stacks (Max_Size => 20,
                                        Element_Type => Bookmark_Type);
 -- Allows the user to jump between recorded locations in a document

Verwenden einer Instanz eines generischen Pakets:

 type Document_Type is record
    Contents : Ada.Strings.Unbounded.Unbounded_String;
    Bookmarks : Bookmark_Stacks.Stack;
 end record;

 procedure Edit (Document_Name : in String) is
   Document : Document_Type;
 begin
   -- Initialise the stack of bookmarks:
   Bookmark_Stacks.Create (S => Document.Bookmarks, Initial_Size => 10);
   -- Now, open the file Document_Name and read it in...
 end Edit;
Vor- und Nachteile[edit]

Die Sprachsyntax ermöglicht die genaue Angabe von Einschränkungen für generische formale Parameter. Beispielsweise kann angegeben werden, dass ein generischer formaler Typ nur einen modularen Typ als den tatsächlichen akzeptiert. Es ist auch möglich, Einschränkungen auszudrücken zwischen generische formale Parameter; zum Beispiel:

 generic
    type Index_Type is (<>); -- must be a discrete type
    type Element_Type is private; -- can be any nonlimited type
    type Array_Type is array (Index_Type range <>) of Element_Type;

In diesem Beispiel wird Array_Type sowohl durch Index_Type als auch durch Element_Type eingeschränkt. Beim Instanziieren der Einheit muss der Programmierer einen tatsächlichen Array-Typ übergeben, der diese Einschränkungen erfüllt.

Der Nachteil dieses feinkörnigen Steuerelements ist eine komplizierte Syntax. Da jedoch alle generischen formalen Parameter vollständig in der Spezifikation definiert sind, kann der Compiler Generika instanziieren, ohne den Hauptteil des Generikums zu betrachten.

Im Gegensatz zu C ++ erlaubt Ada keine speziellen generischen Instanzen und erfordert, dass alle Generika explizit instanziiert werden. Diese Regeln haben mehrere Konsequenzen:

  • Der Compiler kann implementieren gemeinsame Generika: Der Objektcode für eine generische Einheit kann von allen Instanzen gemeinsam genutzt werden (es sei denn, der Programmierer fordert natürlich das Inlining von Unterprogrammen an). Als weitere Konsequenzen:
    • Es gibt keine Möglichkeit zum Aufblähen von Code (Aufblähen von Code ist in C ++ üblich und erfordert besondere Sorgfalt, wie unten erläutert).
    • Es ist möglich, Generika sowohl zur Laufzeit als auch zur Kompilierungszeit zu instanziieren, da für eine neue Instanz kein neuer Objektcode erforderlich ist.
    • tatsächliche Objekte, die einem generischen formalen Objekt entsprechen, werden innerhalb des generischen Objekts immer als nicht statisch betrachtet. sehen Generische formale Objekte im Wikibook für Details und Konsequenzen.
  • Da alle Instanzen eines Generikums genau gleich sind, ist es einfacher, von anderen geschriebene Programme zu überprüfen und zu verstehen. Es sind keine “Sonderfälle” zu berücksichtigen.
  • Da alle Instanziierungen explizit sind, gibt es keine versteckten Instanziierungen, die das Verständnis des Programms erschweren könnten.
  • Ada erlaubt keine “Template-Metaprogrammierung”, da es keine Spezialisierungen erlaubt.

Vorlagen in C ++[edit]

C ++ verwendet Vorlagen, um generische Programmiertechniken zu aktivieren. Die C ++ – Standardbibliothek enthält die Standardvorlagenbibliothek (STL), die ein Framework von Vorlagen für allgemeine Datenstrukturen und Algorithmen bereitstellt. Vorlagen in C ++ können auch für die Metaprogrammierung von Vorlagen verwendet werden. Auf diese Weise kann ein Teil des Codes zur Kompilierungszeit und nicht zur Laufzeit vorab ausgewertet werden. Bei Verwendung der Vorlagenspezialisierung gelten C ++ – Vorlagen als vollständig.

Technische Übersicht[edit]

Es gibt zwei Arten von Vorlagen: Funktionsvorlagen und Klassenvorlagen. EIN Funktionsvorlage ist ein Muster zum Erstellen gewöhnlicher Funktionen basierend auf den Parametrertypen, die beim Instanziieren bereitgestellt werden. Beispielsweise enthält die C ++ Standardvorlagenbibliothek die Funktionsvorlage max (x, y) das schafft Funktionen, die entweder zurückgeben x oder y, je nachdem, welcher Wert größer ist. max() könnte so definiert werden:

template <typename T>
T max(T x, T y) {
  return x < y ? y : x;
}

Spezialisierungen Instanziierungen mit bestimmten Typen dieser Funktionsvorlage können wie eine normale Funktion aufgerufen werden:

std::cout << max(3, 7);  // Outputs 7.

Der Compiler untersucht die zum Aufrufen verwendeten Argumente max und stellt fest, dass dies ein Aufruf an ist max (int, int). Anschließend wird eine Version der Funktion instanziiert, in der der Parametrertyp angegeben ist T. ist int, was der folgenden Funktion entspricht:

int max(int x, int y) {
  return x < y ? y : x;
}

Dies funktioniert unabhängig davon, ob die Argumente x und y sind Ganzzahlen, Zeichenfolgen oder andere Typen, für die der Ausdruck gilt x ist sinnvoll oder genauer gesagt für jeden Typ, für den Operator ist definiert. Eine gemeinsame Vererbung ist für die Menge der Typen, die verwendet werden können, nicht erforderlich und daher der Ententypisierung sehr ähnlich. Ein Programm, das einen benutzerdefinierten Datentyp definiert, kann mithilfe der Operatorüberladung die Bedeutung von definieren < für diesen Typ, so dass seine Verwendung mit dem max () Funktionsvorlage. Während dies in diesem isolierten Beispiel als geringfügiger Vorteil erscheint, ermöglicht es dem Programmierer im Kontext einer umfassenden Bibliothek wie der STL, umfangreiche Funktionen für einen neuen Datentyp zu erhalten, indem nur einige Operatoren dafür definiert werden. Nur definieren < ermöglicht die Verwendung eines Typs mit dem Standard Sortieren(), Stable_Sort (), und binäre Suche() Algorithmen oder in Datenstrukturen wie z einstellens, Heaps und assoziative Arrays.

C ++ - Vorlagen sind zur Kompilierungszeit vollständig typsicher. Zur Demonstration der Standardtyp Komplex definiert nicht die < Operator, weil es keine strikte Reihenfolge für komplexe Zahlen gibt. Deshalb, max (x, y) schlägt mit einem Kompilierungsfehler fehl, wenn x und y sind Komplex Werte. Ebenso andere Vorlagen, die sich darauf verlassen < kann nicht angewendet werden Komplex Daten, sofern kein Vergleich (in Form eines Funktors oder einer Funktion) vorliegt. ZB: A. Komplex kann nicht als Schlüssel für a verwendet werden Karte es sei denn, ein Vergleich ist vorgesehen. Leider generieren Compiler in der Vergangenheit etwas esoterische, lange und nicht hilfreiche Fehlermeldungen für diese Art von Fehler. Wenn Sie sicherstellen, dass ein bestimmtes Objekt einem Methodenprotokoll entspricht, kann dieses Problem behoben werden. Sprachen, die verwenden vergleichen Sie anstatt < kann auch verwenden Komplex Werte als Schlüssel.

Die zweite Art von Vorlage, a Klassenvorlage, erweitert das gleiche Konzept auf Klassen. Eine Klassenvorlagenspezialisierung ist eine Klasse. Klassenvorlagen werden häufig verwendet, um generische Container zu erstellen. Beispielsweise verfügt die STL über einen verknüpften Listencontainer. Um eine verknüpfte Liste von ganzen Zahlen zu erstellen, schreibt man aufführen. Eine Liste von Zeichenfolgen wird angegeben aufführen. EIN aufführen ist eine Reihe von Standardfunktionen zugeordnet, die für alle kompatiblen Parametrertypen funktionieren.

Vorlagenspezialisierung[edit]

Eine leistungsstarke Funktion der C ++ - Vorlagen ist Vorlagenspezialisierung. Dies ermöglicht die Bereitstellung alternativer Implementierungen basierend auf bestimmten Merkmalen des parametrisierten Typs, der instanziiert wird. Die Spezialisierung von Vorlagen dient zwei Zwecken: Ermöglichen bestimmter Formen der Optimierung und Reduzieren des Aufblähens von Code.

Betrachten Sie zum Beispiel a Sortieren() Vorlagenfunktion. Eine der Hauptaktivitäten einer solchen Funktion besteht darin, die Werte an zwei Positionen des Containers auszutauschen oder auszutauschen. Wenn die Werte groß sind (in Bezug auf die Anzahl der Bytes, die zum Speichern der einzelnen Bytes erforderlich sind), ist es häufig schneller, zuerst eine separate Liste von Zeigern auf die Objekte zu erstellen, diese Zeiger zu sortieren und dann die endgültige sortierte Sequenz zu erstellen . Wenn die Werte recht klein sind, ist es normalerweise am schnellsten, die Werte nach Bedarf auszutauschen. Wenn der parametrisierte Typ bereits einen Zeigertyp aufweist, muss kein separates Zeigerarray erstellt werden. Die Vorlagenspezialisierung ermöglicht es dem Vorlagenersteller, verschiedene Implementierungen zu schreiben und die Merkmale anzugeben, die die parametrisierten Typen für jede zu verwendende Implementierung aufweisen müssen.

Im Gegensatz zu Funktionsvorlagen können Klassenvorlagen teilweise spezialisiert sein. Dies bedeutet, dass eine alternative Version des Klassenvorlagencodes bereitgestellt werden kann, wenn einige der Vorlagenparameter bekannt sind, während andere Vorlagenparameter generisch bleiben. Dies kann zum Beispiel verwendet werden, um eine Standardimplementierung zu erstellen (die Hauptspezialisierung), bei dem davon ausgegangen wird, dass das Kopieren eines Parametrertyps teuer ist, und dann Teilspezialisierungen für Typen erstellt werden, deren Kopieren billig ist, wodurch die Gesamteffizienz erhöht wird. Clients einer solchen Klassenvorlage verwenden lediglich Spezialisierungen davon, ohne wissen zu müssen, ob der Compiler die primäre Spezialisierung oder jeweils eine teilweise Spezialisierung verwendet hat. Klassenvorlagen können auch sein voll spezialisiert, Dies bedeutet, dass eine alternative Implementierung bereitgestellt werden kann, wenn alle Parametrisierungstypen bekannt sind.

Vorteile und Nachteile[edit]

Einige Verwendungen von Vorlagen, wie z max () Funktion, wurden zuvor von funktionsähnlichen Präprozessor-Makros (ein Erbe der Programmiersprache C) gefüllt. Zum Beispiel ist hier eine möglich max () Makro:

#define max(a,b) ((a) < (b) ? (b) : (a))

Makros werden vor der eigentlichen Kompilierung vom Präprozessor erweitert. Vorlagen werden zur Kompilierungszeit erweitert. Makros werden immer inline erweitert. Vorlagen können auch als Inline-Funktionen erweitert werden, wenn der Compiler dies für angemessen hält. Somit haben sowohl funktionsähnliche Makros als auch Funktionsvorlagen keinen Laufzeitaufwand.

Vorlagen werden jedoch im Allgemeinen als Verbesserung gegenüber Makros für diese Zwecke angesehen. Vorlagen sind typsicher. Vorlagen vermeiden einige der häufigsten Fehler im Code, bei denen funktionsähnliche Makros häufig verwendet werden, z. B. das zweimalige Auswerten von Parametern mit Nebenwirkungen. Am wichtigsten ist vielleicht, dass Vorlagen so konzipiert wurden, dass sie auf viel größere Probleme als Makros anwendbar sind.

Die Verwendung von Vorlagen weist vier Hauptnachteile auf: unterstützte Funktionen, Compilerunterstützung, schlechte Fehlermeldungen und Aufblähen des Codes:

  1. In Vorlagen in C ++ fehlen viele Funktionen, weshalb die Implementierung und einfache Verwendung oft unmöglich ist. Stattdessen müssen sich Programmierer auf komplizierte Tricks verlassen, die zu aufgeblähtem, schwer verständlichem und schwer zu pflegendem Code führen. Aktuelle Entwicklungen in den C ++ - Standards verschärfen dieses Problem, indem sie diese Tricks intensiv nutzen und viele neue Funktionen für Vorlagen darauf oder unter Berücksichtigung dieser erstellen.
  2. Viele Compiler haben in der Vergangenheit eine schlechte Unterstützung für Vorlagen, daher kann die Verwendung von Vorlagen dazu führen, dass Code etwas weniger portabel ist. Die Unterstützung kann auch schlecht sein, wenn ein C ++ - Compiler mit einem Linker verwendet wird, der C ++ nicht kennt, oder wenn versucht wird, Vorlagen über gemeinsam genutzte Bibliotheksgrenzen hinweg zu verwenden. Die meisten modernen Compiler verfügen jedoch jetzt über eine ziemlich robuste und standardmäßige Vorlagenunterstützung, und der neue C ++ - Standard C ++ 11 behebt diese Probleme weiter.
  3. Fast alle Compiler erzeugen verwirrende, lange oder manchmal nicht hilfreiche Fehlermeldungen, wenn Fehler in Code erkannt werden, der Vorlagen verwendet.[18] Dies kann die Entwicklung von Vorlagen erschweren.
  4. Schließlich erfordert die Verwendung von Vorlagen, dass der Compiler eine separate generiert Beispiel der Vorlagenklasse oder -funktion für jede Permutation der damit verwendeten Typparameter. (Dies ist erforderlich, da Typen in C ++ nicht alle dieselbe Größe haben und die Größe der Datenfelder für die Funktionsweise von Klassen wichtig ist.) Die wahllose Verwendung von Vorlagen kann daher zu einem Aufblähen des Codes führen, was zu übermäßig großen ausführbaren Dateien führt. Die umsichtige Verwendung der Spezialisierung und Ableitung von Vorlagen kann jedoch in einigen Fällen das Aufblähen von Code drastisch reduzieren:

Kann die Ableitung verwendet werden, um das Problem des replizierten Codes zu verringern, da Vorlagen verwendet werden? Dies würde das Ableiten einer Vorlage von einer gewöhnlichen Klasse beinhalten. Diese Technik hat sich als erfolgreich erwiesen, um das Aufblähen von Code im realen Gebrauch einzudämmen. Menschen, die eine solche Technik nicht verwenden, haben festgestellt, dass replizierter Code selbst in Programmen mittlerer Größe Megabyte Code-Speicherplatz kosten kann.

Die zusätzlichen Instanziierungen, die von Vorlagen generiert werden, können auch dazu führen, dass Debugger Schwierigkeiten haben, ordnungsgemäß mit Vorlagen zu arbeiten. Beispielsweise kann das Festlegen eines Debug-Haltepunkts in einer Vorlage aus einer Quelldatei entweder das Festlegen des Haltepunkts in der tatsächlich gewünschten Instanziierung verfehlen oder an jeder Stelle, an der die Vorlage instanziiert wird, einen Haltepunkt festlegen.

Da der Compiler makroähnliche Erweiterungen von Vorlagen durchführen und zur Kompilierungszeit verschiedene Instanzen davon generieren muss, muss der Implementierungsquellcode für die Vorlagenklasse oder -funktion für den Code verfügbar sein (z. B. in einem Header enthalten). Vorlagenklassen oder -funktionen, einschließlich eines Großteils der Standardvorlagenbibliothek (STL), können nicht kompiliert werden, wenn sie nicht in Headerdateien enthalten sind. (Dies steht im Gegensatz zu Code ohne Vorlagen, der möglicherweise binär kompiliert wird und nur eine Deklarationsheaderdatei für den Code bereitstellt, der ihn verwendet.) Dies kann ein Nachteil sein, indem der implementierende Code verfügbar gemacht wird, wodurch einige Abstraktionen entfernt werden und dessen Einschränkung eingeschränkt werden kann Verwendung in Closed-Source-Projekten.[citation needed]

Vorlagen in D.[edit]

Die Programmiersprache D unterstützt Vorlagen, die auf C ++ basieren. Die meisten C ++ - Vorlagen-Idiome werden ohne Änderung auf D übertragen, D fügt jedoch einige zusätzliche Funktionen hinzu:

  • Vorlagenparameter in D sind nicht nur auf Typen und primitive Werte beschränkt, sondern ermöglichen auch beliebige Werte zur Kompilierungszeit (wie Zeichenfolgen und Strukturliterale) und Aliase für beliebige Bezeichner, einschließlich anderer Vorlagen oder Vorlageninstanziierungen.
  • Vorlageneinschränkungen und die statisch wenn Anweisungen bieten eine Alternative zu C ++ 's Substitutionsfehler ist kein Fehlermechanismus (SFINAE), ähnlich wie bei C ++ - Konzepten.
  • Das ist (...) Mit expression kann die spekulative Instanziierung die Eigenschaften eines Objekts zur Kompilierungszeit überprüfen.
  • Das Auto Schlüsselwort und die eine Art von Ausdruck ermöglicht Typinferenz für Variablendeklarationen und Funktionsrückgabewerte, was wiederum "Voldemort-Typen" (Typen ohne globalen Namen) ermöglicht.[20]

Vorlagen in D verwenden eine andere Syntax als in C ++: In C ++ werden Vorlagenparameter in eckige Klammern gesetzt (Vorlage), D verwendet ein Ausrufezeichen und Klammern: Vorlage! (Param1, param2). Dies vermeidet die C ++ - Parsing-Schwierigkeiten aufgrund von Mehrdeutigkeiten mit Vergleichsoperatoren. Wenn nur ein Parameter vorhanden ist, können die Klammern weggelassen werden.

Herkömmlicherweise kombiniert D die obigen Merkmale, um einen Polymorphismus zur Kompilierungszeit unter Verwendung einer auf Merkmalen basierenden generischen Programmierung bereitzustellen. Ein Eingabebereich ist beispielsweise als ein Typ definiert, der die von durchgeführten Prüfungen erfüllt isInputRange, die wie folgt definiert ist:

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    (inout int = 0)
    {
        R r = R.init;     // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}

Eine Funktion, die nur Eingabebereiche akzeptiert, kann dann die obige Vorlage in einer Vorlageneinschränkung verwenden:

auto fun(Range)(Range range)
    if (isInputRange!Range)
{
    // ...
}
Codegenerierung[edit]

Zusätzlich zur Metaprogrammierung von Vorlagen bietet D verschiedene Funktionen, um die Codegenerierung zur Kompilierungszeit zu ermöglichen:

  • Das importieren Mit expression können Sie eine Datei von der Festplatte lesen und ihren Inhalt als Zeichenfolgenausdruck verwenden.
  • Durch die Reflexion zur Kompilierungszeit können Deklarationen und ihre Mitglieder während der Kompilierung aufgelistet und überprüft werden.
  • Benutzerdefinierte Attribute ermöglichen es Benutzern, Deklarationen mit beliebigen Bezeichnern zu versehen, die dann mithilfe der Reflexion zur Kompilierungszeit aufgelistet werden können.
  • Mit der CTFE (Compile-Time Function Execution) kann eine Teilmenge von D (auf sichere Operationen beschränkt) während der Kompilierung interpretiert werden.
  • String-Mixins ermöglichen das Auswerten und Kompilieren des Inhalts eines String-Ausdrucks als D-Code, der Teil des Programms wird.

Durch die Kombination der oben genannten Funktionen kann Code basierend auf vorhandenen Deklarationen generiert werden. Beispielsweise können D-Serialisierungs-Frameworks die Mitglieder eines Typs auflisten und spezielle Funktionen für jeden serialisierten Typ generieren, um Serialisierung und Deserialisierung durchzuführen. Benutzerdefinierte Attribute können ferner Serialisierungsregeln anzeigen.

Das importieren Die Ausführung von Ausdrucks- und Kompilierungsfunktionen ermöglicht auch die effiziente Implementierung domänenspezifischer Sprachen. Wenn für eine Funktion beispielsweise eine Zeichenfolge verwendet wird, die eine HTML-Vorlage enthält und den entsprechenden D-Quellcode zurückgibt, kann sie folgendermaßen verwendet werden:

// Import the contents of example.htt as a string manifest constant.
enum htmlTemplate = import("example.htt");

// Transpile the HTML template to D code.
enum htmlDCode = htmlTemplateToD(htmlTemplate);

// Paste the contents of htmlDCode as D code.
mixin(htmlDCode);

Großzügigkeit in Eiffel[edit]

Generische Klassen sind seit der ursprünglichen Methode und dem ursprünglichen Sprachdesign ein Teil von Eiffel. Die Stiftungsveröffentlichungen von Eiffel,[21][22] benutze den Begriff Großzügigkeit um die Erstellung und Verwendung generischer Klassen zu beschreiben.

Grundlegende / uneingeschränkte Großzügigkeit[edit]

Generische Klassen werden mit ihrem Klassennamen und einer Liste von einer oder mehreren deklariert formale generische Parameter. Im folgenden Code Klasse LIST hat einen formalen generischen Parameter G

class
    LIST [G]
            ...
feature   -- Access
    item: G
            -- The item currently pointed to by cursor
            ...
feature   -- Element change
    put (new_item: G)
            -- Add `new_item' at the end of the list
            ...

Die formalen generischen Parameter sind Platzhalter für beliebige Klassennamen, die angegeben werden, wenn eine Deklaration der generischen Klasse erfolgt, wie in den beiden gezeigt generische Ableitungen unten, wo ACCOUNT und DEPOSIT sind andere Klassennamen. ACCOUNT und DEPOSIT gelten als tatsächliche generische Parameter da sie echte Klassennamen liefern, die ersetzt werden können G im tatsächlichen Gebrauch.

    list_of_accounts: LIST [ACCOUNT]
            -- Account list

    list_of_deposits: LIST [DEPOSIT]
            -- Deposit list

Innerhalb des Eiffeltypsystems, obwohl Klasse LIST [G] wird als Klasse betrachtet, es wird nicht als Typ betrachtet. Eine generische Ableitung von LIST [G] sowie LIST [ACCOUNT] wird als Typ angesehen.

Eingeschränkte Großzügigkeit[edit]

Für die oben gezeigte Listenklasse ein tatsächlicher generischer Parameter, der ersetzt G kann jede andere verfügbare Klasse sein. Um die Menge der Klassen einzuschränken, aus denen gültige generische Parameter ausgewählt werden können, a generische Einschränkung kann angegeben werden. In der Klassenerklärung SORTED_LIST Im Folgenden schreibt die generische Einschränkung vor, dass jeder gültige tatsächliche generische Parameter eine Klasse ist, die von der Klasse erbt COMPARABLE. Die generische Einschränkung stellt sicher, dass Elemente von a SORTED_LIST kann in der Tat sortiert werden.

class
    SORTED_LIST [G -> COMPARABLE]

Generika in Java[edit]

Unterstützung für die Generika, oder "Container vom Typ T" wurde der Java-Programmiersprache im Jahr 2004 als Teil von J2SE 5.0 hinzugefügt. In Java werden Generika nur zur Kompilierungszeit auf Typkorrektheit überprüft. Die generischen Typinformationen werden dann über einen Prozess namens Typlöschung entfernt, um die Kompatibilität mit alten JVM-Implementierungen zu gewährleisten und sie zur Laufzeit nicht verfügbar zu machen. Zum Beispiel a Aufführen wird in den Rohtyp konvertiert Aufführen. Der Compiler fügt Typumwandlungen ein, um die Elemente in die zu konvertieren String Geben Sie ein, wenn sie aus der Liste abgerufen werden, wodurch die Leistung im Vergleich zu anderen Implementierungen wie C ++ - Vorlagen verringert wird.

Generizität in .NET [C#, VB.NET][edit]

Generika wurden im November 2005 als Teil von .NET Framework 2.0 hinzugefügt, basierend auf einem 1999 gestarteten Forschungsprototyp von Microsoft Research.[23] Obwohl .NET-Generika den Generika in Java ähnlich sind, wenden sie keine Typlöschung an, sondern implementieren Generika als erstklassigen Mechanismus zur Laufzeit mithilfe der Reifizierung. Diese Entwurfsauswahl bietet zusätzliche Funktionen, z. B. das Ermöglichen der Reflexion unter Beibehaltung generischer Typen sowie das Verringern einiger Einschränkungen beim Löschen (z. B. das Nichterstellen generischer Arrays).[24][25] Dies bedeutet auch, dass Laufzeit-Casts und normalerweise teure Box-Conversions keine Leistungseinbußen verursachen. Wenn primitive und Werttypen als generische Argumente verwendet werden, erhalten sie spezielle Implementierungen, die effiziente generische Sammlungen und Methoden ermöglichen. Wie in C ++ und Java verschachtelte generische Typen wie Dictionary> sind gültige Typen, von denen jedoch für Mitgliedssignaturen in Code-Design-Entwurfsregeln abgeraten wird.[26]

.NET erlaubt sechs Arten von generischen Typeinschränkungen unter Verwendung von wo Schlüsselwort einschließlich der Einschränkung generischer Typen auf Werttypen, Klassen, Konstruktoren und die Implementierung von Schnittstellen.[27] Unten finden Sie ein Beispiel mit einer Schnittstellenbeschränkung:

 1 using System;
 2 
 3 class Sample
 4 {
 5     static void Main()
 6     {
 7         int[] array = { 0, 1, 2, 3 };
 8         MakeAtLeast<int>(array, 2); // Change array to { 2, 2, 2, 3 }
 9         foreach (int i in array)
10             Console.WriteLine(i); // Print results.
11         Console.ReadKey(true);
12     }
13 
14     static void MakeAtLeast<T>(T[] list, T lowest) where T : IComparable<T>
15     {
16         for (int i = 0; i < list.Length; i++)
17             if (list[i].CompareTo(lowest) < 0)
18                 list[i] = lowest;
19     }
20 }

Das MakeAtLeast () Die Methode ermöglicht den Betrieb von Arrays mit Elementen vom generischen Typ T.. Die Typbeschränkung der Methode gibt an, dass die Methode auf jeden Typ anwendbar ist T. das implementiert das generische Vergleichbar Schnittstelle. Dies stellt einen Fehler bei der Kompilierung sicher, wenn die Methode aufgerufen wird, wenn der Typ keinen Vergleich unterstützt. Die Schnittstelle bietet die generische Methode Vergleiche mit (T).

Die obige Methode könnte auch ohne generische Typen geschrieben werden, einfach unter Verwendung der nicht generischen Array Art. Da Arrays jedoch kontravariant sind, wäre das Casting nicht typsicher, und der Compiler könnte bestimmte mögliche Fehler nicht finden, die sonst bei der Verwendung generischer Typen abgefangen würden. Außerdem müsste die Methode stattdessen als Objekte auf die Array-Elemente zugreifen und zum Vergleichen zweier Elemente ein Casting erfordern. (Für Werttypen wie Typen wie int Dies erfordert eine Box-Konvertierung, obwohl dies mit dem umgangen werden kann Vergleicher Klasse, wie in den Standard-Auflistungsklassen.)

Ein bemerkenswertes Verhalten statischer Elemente in einer generischen .NET-Klasse ist die Instanziierung statischer Elemente pro Laufzeittyp (siehe Beispiel unten).

    //A generic class
    public class GenTest<T>
    {
        //A static variable - will be created for each type on reflection
        static CountedInstances OnePerType = new CountedInstances();

        //a data member
        private T mT;

        //simple constructor
        public GenTest(T pT)
        {
            mT = pT;
        }
    }

    //a class
    public class CountedInstances
    {
        //Static variable - this will be incremented once per instance
        public static int Counter;

        //simple constructor
        public CountedInstances()
        {
            //increase counter by one during object instantiation
            CountedInstances.Counter++;
        }
    }

  //main code entry point
  //at the end of execution, CountedInstances.Counter = 2
  GenTest<int> g1 = new GenTest<int>(1);
  GenTest<int> g11 = new GenTest<int>(11);
  GenTest<int> g111 = new GenTest<int>(111);
  GenTest<double> g2 = new GenTest<double>(1.0);

Großzügigkeit in Delphi[edit]

Der Object Pascal-Dialekt von Delphi hat in der Delphi 2007-Version Generika erworben, zunächst nur mit dem (jetzt nicht mehr erhältlichen) .NET-Compiler, bevor er in der Delphi 2009-Version zum nativen Code hinzugefügt wurde. Die Semantik und Funktionen von Delphi-Generika orientieren sich weitgehend an denen von Generika in .NET 2.0, obwohl die Implementierung notwendigerweise ganz anders ist. Hier ist eine mehr oder weniger direkte Übersetzung des ersten oben gezeigten C # -Beispiels:

program Sample;

{$APPTYPE CONSOLE}

uses
  Generics.Defaults; //for IComparer<>

type
  TUtils = class
    class procedure MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T;
      Comparer: IComparer<T>); overload;
    class procedure MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T); overload;
  end;

class procedure TUtils.MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T;
  Comparer: IComparer<T>);
var
  I: Integer;
begin
  if Comparer = nil then Comparer := TComparer<T>.Default;
  for I := Low(Arr) to High(Arr) do
    if Comparer.Compare(Arr[I], Lowest) < 0 then
      Arr[I] := Lowest;
end;

class procedure TUtils.MakeAtLeast<T>(Arr: TArray<T>; const Lowest: T);
begin
  MakeAtLeast<T>(Arr, Lowest, nil);
end;

var
  Ints: TArray<Integer>;
  Value: Integer;
begin
  Ints := TArray<Integer>.Create(0, 1, 2, 3);
  TUtils.MakeAtLeast<Integer>(Ints, 2);
  for Value in Ints do
    WriteLn(Value);
  ReadLn;
end.

Wie bei C # können sowohl Methoden als auch ganze Typen einen oder mehrere Typparameter haben. In diesem Beispiel ist TArray ein generischer Typ (definiert durch die Sprache) und MakeAtLeast eine generische Methode. Die verfügbaren Einschränkungen sind den verfügbaren Einschränkungen in C # sehr ähnlich: jeder Werttyp, jede Klasse, eine bestimmte Klasse oder Schnittstelle und eine Klasse mit einem parameterlosen Konstruktor. Mehrere Einschränkungen wirken als additive Vereinigung.

Großzügigkeit in Free Pascal[edit]

Free Pascal implementierte Generika vor Delphi und mit unterschiedlicher Syntax und Semantik. Seit FPC Version 2.6.0 ist die Syntax im Delphi-Stil jedoch verfügbar, wenn der Sprachmodus {$ mode Delphi} verwendet wird. So können Free Pascal-Programmierer Generika in jedem von ihnen bevorzugten Stil verwenden.

Delphi und Free Pascal Beispiel:

// Delphi style
unit A;

{$ifdef fpc}
  {$mode delphi}
{$endif}

interface

type
  TGenericClass<T> = class
    function Foo(const AValue: T): T;
  end;

implementation

function TGenericClass<T>.Foo(const AValue: T): T;
begin
  Result := AValue + AValue;
end;

end.

// Free Pascal's ObjFPC style
unit B;

{$ifdef fpc}
  {$mode objfpc}
{$endif}

interface

type
  generic TGenericClass<T> = class
    function Foo(const AValue: T): T;
  end;

implementation

function TGenericClass.Foo(const AValue: T): T;
begin
  Result := AValue + AValue;
end;

end.

// example usage, Delphi style
program TestGenDelphi;

{$ifdef fpc}
  {$mode delphi}
{$endif}

uses
  A,B;

var
  GC1: A.TGenericClass<Integer>;
  GC2: B.TGenericClass<String>;
begin
  GC1 := A.TGenericClass<Integer>.Create;
  GC2 := B.TGenericClass<String>.Create;
  WriteLn(GC1.Foo(100)); // 200
  WriteLn(GC2.Foo('hello')); // hellohello
  GC1.Free;
  GC2.Free;
end.

// example usage, ObjFPC style
program TestGenDelphi;

{$ifdef fpc}
  {$mode objfpc}
{$endif}

uses
  A,B;

// required in ObjFPC
type
  TAGenericClassInt = specialize A.TGenericClass<Integer>;
  TBGenericClassString = specialize B.TGenericClass<String>;
var
  GC1: TAGenericClassInt;
  GC2: TBGenericClassString;
begin
  GC1 := TAGenericClassInt.Create;
  GC2 := TBGenericClassString.Create;
  WriteLn(GC1.Foo(100)); // 200
  WriteLn(GC2.Foo('hello')); // hellohello
  GC1.Free;
  GC2.Free;
end.

Funktionssprachen[edit]

Großzügigkeit in Haskell[edit]

Der Typklassenmechanismus von Haskell unterstützt die generische Programmierung. Sechs der vordefinierten Typklassen in Haskell (einschließlich Gl, die Typen, die auf Gleichheit verglichen werden können, und ShowDie Typen, deren Werte als Zeichenfolgen gerendert werden können, haben die besondere Eigenschaft, sie zu unterstützen abgeleitete Instanzen. Dies bedeutet, dass ein Programmierer, der einen neuen Typ definiert, angeben kann, dass dieser Typ eine Instanz einer dieser speziellen Typklassen sein soll, ohne Implementierungen der Klassenmethoden bereitzustellen, wie dies normalerweise beim Deklarieren von Klasseninstanzen erforderlich ist. Alle erforderlichen Methoden werden basierend auf der Struktur des Typs "abgeleitet", dh automatisch erstellt. In der folgenden Deklaration eines Typs von Binärbäumen heißt es beispielsweise, dass es sich um eine Instanz der Klassen handeln soll Gl und Show::

data BinTree a = Leaf a | Node (BinTree a) a (BinTree a)
      deriving (Eq, Show)

Dies führt zu einer Gleichheitsfunktion (==) und eine Zeichenfolgendarstellungsfunktion (Show) wird automatisch für jeden Typ des Formulars definiert BinTree T. unter der Vorraussetzung, dass T. selbst unterstützt diese Operationen.

Die Unterstützung für abgeleitete Instanzen von Gl und Show macht ihre Methoden == und Show generisch auf qualitativ andere Weise als parametrisch polymorphe Funktionen: Diese "Funktionen" (genauer gesagt typindizierte Familien von Funktionen) können auf Werte verschiedener Typen angewendet werden, und obwohl sie sich für jeden Argumenttyp unterschiedlich verhalten, ist wenig Arbeit erforderlich benötigt, um Unterstützung für einen neuen Typ hinzuzufügen. Ralf Hinze (2004) hat gezeigt, dass mit bestimmten Programmiertechniken ein ähnlicher Effekt für benutzerdefinierte Typklassen erzielt werden kann. Andere Forscher haben Ansätze für diese und andere Arten von Generizität im Zusammenhang mit Haskell und Erweiterungen für Haskell vorgeschlagen (siehe unten).

Polyp[edit]

PolyP war die erste generische Programmiersprachenerweiterung für Haskell. In PolyP werden generische Funktionen aufgerufen polytypisch. Die Sprache führt ein spezielles Konstrukt ein, in dem solche polytypischen Funktionen durch strukturelle Induktion über die Struktur des Musterfunktors eines regulären Datentyps definiert werden können. Regelmäßige Datentypen in PolyP sind eine Teilmenge der Haskell-Datentypen. Ein regulärer Datentyp t muss von Art sein * → *, und wenn ein ist das formale Typargument in der Definition, dann alle rekursiven Aufrufe an t muss das Formular haben ta. Diese Einschränkungen schließen höherwertige Datentypen sowie verschachtelte Datentypen aus, bei denen die rekursiven Aufrufe eine andere Form haben. Die Abflachungsfunktion in PolyP wird hier als Beispiel bereitgestellt:

   flatten :: Regular d => d a -> [a]
   flatten = cata fl

   polytypic fl :: f a [a] -> [a]
     case f of
       g+h -> either fl fl
       g*h -> (x,y) -> fl x ++ fl y
       () -> x -> []
       Par -> x -> [x]
       Rec -> x -> x
       d@g -> concat . flatten . pmap fl
       Con t -> x -> []

   cata :: Regular d => (FunctorOf d a b -> b) -> d a -> b
Generisches Haskell[edit]

Generic Haskell ist eine weitere Erweiterung von Haskell, die an der Universität Utrecht in den Niederlanden entwickelt wurde. Die Erweiterungen sind:

  • Typindizierte Werte werden als Wert definiert, der über die verschiedenen Haskell-Typkonstruktoren (Einheit, primitive Typen, Summen, Produkte und benutzerdefinierte Typkonstruktoren) indiziert wird. Darüber hinaus können wir auch das Verhalten typindizierter Werte für einen bestimmten Konstruktor mithilfe von angeben Konstruktorfälleund verwenden Sie eine generische Definition in einer anderen mit Standardfälle.

Der resultierende typindizierte Wert kann auf jeden Typ spezialisiert werden.

  • Art-indizierte Typen sind Typen, die über Arten indiziert sind und durch Angabe eines Falls für beide definiert werden * * und k → k '. Instanzen werden erhalten, indem der artindizierte Typ auf eine Art angewendet wird.
  • Generische Definitionen können verwendet werden, indem sie auf einen Typ oder eine Art angewendet werden. Das nennt man generische Anwendung. Das Ergebnis ist ein Typ oder Wert, je nachdem, welche Art von generischer Definition angewendet wird.
  • Generische Abstraktion ermöglicht die Definition generischer Definitionen durch Abstraktion eines Typparameters (einer bestimmten Art).
  • Typindizierte Typen sind Typen, die über die Typkonstruktoren indiziert sind. Diese können verwendet werden, um komplexeren generischen Werten Typen zuzuweisen. Die resultierenden typindizierten Typen können auf jeden Typ spezialisiert werden.

Als Beispiel die Gleichheitsfunktion in Generic Haskell:[28]

   type Eq {[ * ]} t1 t2 = t1 -> t2 -> Bool
   type Eq {[ k -> l ]} t1 t2 = forall u1 u2. Eq {[ k ]} u1 u2 -> Eq {[ l ]} (t1 u1) (t2 u2)

   eq {| t :: k |} :: Eq {[ k ]} t t
   eq {| Unit |} _ _ = True
   eq {| :+: |} eqA eqB (Inl a1) (Inl a2) = eqA a1 a2
   eq {| :+: |} eqA eqB (Inr b1) (Inr b2) = eqB b1 b2
   eq {| :+: |} eqA eqB _ _ = False
   eq {| :*: |} eqA eqB (a1 :*: b1) (a2 :*: b2) = eqA a1 a2 && eqB b1 b2
   eq {| Int |} = (==)
   eq {| Char |} = (==)
   eq {| Bool |} = (==)

Sauber[edit]

Clean bietet generisches programmierbasiertes PolyP und das generische Haskell, wie vom GHC> = 6.0 unterstützt. Es parametrisiert nach Art, bietet aber eine Überlastung.

Andere Sprachen[edit]

Die ML-Familie von Programmiersprachen unterstützt die generische Programmierung durch parametrischen Polymorphismus und generische Module Funktoren.
Sowohl Standard ML als auch OCaml bieten Funktoren, die Klassenvorlagen und Adas generischen Paketen ähneln. Schemasyntaktische Abstraktionen haben auch einen Zusammenhang mit der Generizität - dies ist in der Tat eine Obermenge von Vorlagen à la C ++.

Ein Verilog-Modul kann einen oder mehrere Parameter annehmen, denen ihre tatsächlichen Werte bei der Instanziierung des Moduls zugewiesen werden. Ein Beispiel ist ein generisches Registerarray, bei dem die Arraybreite über einen Parameter angegeben wird. Ein solches Array kann in Kombination mit einem generischen Drahtvektor aus einer einzelnen Modulimplementierung ein generisches Puffer- oder Speichermodul mit einer beliebigen Bitbreite machen.[29]

VHDL, das von Ada abgeleitet ist, verfügt auch über allgemeine Funktionen.

Siehe auch[edit]

Verweise[edit]

  1. ^
    Lee, Kent D. (15. Dezember 2008). Programmiersprachen: Ein aktiver Lernansatz. Springer Science & Business Media. S. 9–10. ISBN 978-0-387-79422-8.
  2. ^ Milner, R.; Morris, L.; Newey, M. (1975). "Eine Logik für berechenbare Funktionen mit reflexiven und polymorphen Typen". Tagungsband zur Prüfung und Verbesserung von Programmen.
  3. ^
    Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Designmuster. Addison-Wesley. ISBN 0-201-63361-2.CS1-Wartung: ref = harv (Link)
  4. ^ Musser, David R.; Stepanov, Alexander A. Generische Programmierung (PDF).
  5. ^ Alexander Stepanov; Paul McJones (19. Juni 2009). Elemente der Programmierung. Addison-Wesley Professional. ISBN 978-0-321-63537-2.
  6. ^ Musser, David R.; Stepanov, Alexander A. (1987). "Eine Bibliothek generischer Algorithmen in Ada". Bericht der jährlichen ACM SIGAda International Conference 1987 über Ada: 216–225. CiteSeerX 10.1.1.588.7431. doi:10.1145 / 317500.317529. ISBN 0897912438. S2CID 795406.
  7. ^ Alexander Stepanov und Meng Lee: Die Standardvorlagenbibliothek. HP Laboratories Technical Report 95-11 (R.1), 14. November 1995
  8. ^ Matthew H. Austern: Generische Programmierung und STL: Verwenden und Erweitern der C ++ Standard Template Library. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA 1998
  9. ^ Jeremy G. Siek, Lie-Quan Lee und Andrew Lumsdaine: The Boost Graph Library: Benutzerhandbuch und Referenzhandbuch. Addison-Wesley 2001
  10. ^ Stepanov, Alexander. Kurze Geschichte von STL (PDF).
  11. ^ ein b Stroustrup, Bjarne. Entwicklung einer Sprache in und für die reale Welt: C ++ 1991-2006 (PDF). doi:10.1145 / 1238844.1238848. S2CID 7518369.
  12. ^ Lo Russo, Graziano. "Ein Interview mit A. Stepanov".
  13. ^ Roland Backhouse; Patrik Jansson; Johan Jeuring; Lambert Meertens (1999). Generische Programmierung - eine Einführung (PDF).
  14. ^ Lämmel, Ralf; Peyton Jones, Simon. "Scrap Your Boilerplate: Ein praktisches Entwurfsmuster für die generische Programmierung" (PDF). Microsoft. Abgerufen 16. Oktober 2016.
  15. ^ Gabriel Dos Reis; Jaakko Ja ̈rvi (2005). "Was ist generische Programmierung? (Preprint LCSD'05)" (PDF). Archiviert von das Original (PDF) am 25. Dezember 2005.
  16. ^ R. Garcia; J. Ja ̈rvi; A. Lumsdaine; J. Siek; J. Willcock (2005). "Eine erweiterte vergleichende Studie zur Sprachunterstützung für die generische Programmierung (Preprint)". CiteSeerX 10.1.1.110.122.
  17. ^ Stroustrup, Dos Reis (2003): Konzepte - Entwurfsoptionen für die Überprüfung von Vorlagenargumenten
  18. ^ Stroustrup, Bjarne (1994). "15.5 Vermeiden der Codereplikation". Das Design und die Entwicklung von C ++. Reading, Massachusetts: Addison-Wesley. S. 346–348. Bibcode:1994dec..book ..... S.. ISBN 978-81-317-1608-3.
  19. ^ Hell, Walter. "Voldemort-Typen in D". Dr. Dobbs. Abgerufen 3. Juni 2015.
  20. ^ Objektorientierte Softwarekonstruktion, Prentice Hall, 1988 und Objektorientierte Softwarekonstruktion, 2. Auflage, Prentice Hall, 1997.
  21. ^ Eiffel: Die Sprache, Prentice Hall, 1991.
  22. ^ .NET / C # Generics-Verlauf: Einige Fotos vom Februar 1999
  23. ^ C #: Gestern, heute und morgen: Ein Interview mit Anders Hejlsberg
  24. ^ Generika in C #, Java und C ++
  25. ^ Codeanalyse CA1006: Verschachteln Sie keine generischen Typen in Mitgliedssignaturen
  26. ^ Einschränkungen bei Typparametern (C # -Programmierhandbuch)
  27. ^ Das generische Haskell-Benutzerhandbuch
  28. ^ Verilog anhand eines Beispiels, Abschnitt Der Rest als Referenz. Blaine C. Readler, Full Arc Press, 2011. ISBN 978-0-9834973-0-1

Quellen[edit]

Weiterführende Literatur[edit]

Externe Links[edit]

C ++ / D.
C # /. NET
Delphi / Objekt Pascal
Eiffel
Haskell
Java


after-content-x4