Generisk programmering är ett programmeringsparadigm som består av en sådan beskrivning av data och algoritmer som kan appliceras på olika typer av data utan att ändra själva beskrivningen. I en eller annan form stöds den av olika programmeringsspråk . Generiska programmeringsmöjligheter dök först upp i form av generika (generiska funktioner) på 1970 -talet i Clu- och Ada -språken , sedan som parametrisk polymorfism i ML och dess ättlingar, och sedan i många objektorienterade språk som C++ , Python [ 1] , Java , Object Pascal [2] , D , Eiffel , språk för .NET- plattformen och andra.
Generisk programmering betraktas som en programmeringsmetodik baserad på separation av datastrukturer och algoritmer genom användning av abstrakta kravbeskrivningar [3] . Abstrakta kravdeklarationer är en förlängning av konceptet med en abstrakt datatyp . Istället för att beskriva en enda typ i generisk programmering används en beskrivning av en familj av typer som har ett gemensamt gränssnitt och semantiskt beteende . En uppsättning krav som beskriver ett gränssnitt och semantiskt beteende kallas ett koncept . Således kan en algoritm skriven i en generaliserad stil appliceras på vilken typ som helst som tillfredsställer den med dess koncept. Denna möjlighet kallas polymorfism .
En typ sägs modellera ett koncept (är en modell av ett koncept) om den uppfyller dess krav. Ett koncept är en förfining av ett annat koncept om det kompletterar det senare. Konceptkraven innehåller följande information: [4]
I C++ implementeras OOP genom virtuella funktioner och arv, medan OP (generisk programmering) implementeras genom klass- och funktionsmallar. Kärnan i båda metoderna är dock endast indirekt relaterad till specifika implementeringstekniker. Mer formellt är OOP baserad på subtyp polymorfism , medan OP är baserad på parametrisk polymorfism . På andra språk kan båda implementeras på olika sätt. Till exempel har multimetoder i CLOS semantik som liknar parametrisk polymorfism.
Masser och Stepanov särskiljer följande steg för att lösa problemet enligt OP-metoden:
Minimering och inramning syftar till att skapa en struktur så att algoritmerna är oberoende av specifika datatyper. Detta tillvägagångssätt återspeglas i strukturen för STL - biblioteket . [5]
Ett alternativt tillvägagångssätt för att definiera generisk programmering, som kan kallas generisk programmering av datatyp , föreslogs av Richard Bird och Lambert Meertens . I den är datatypstrukturer parametrar för generiska program. För att göra detta introduceras en ny abstraktionsnivå i programmeringsspråket, nämligen parametrisering med avseende på klasser av algebror med en variabel signatur . Även om teorierna för båda tillvägagångssätten är oberoende av programmeringsspråket, har Musser-Stepanovs synsätt, som betonar konceptanalys, gjort C++ till sin huvudplattform, medan generisk datatypsprogrammering nästan uteslutande används av Haskell och dess varianter [6] .
Generiska programmeringsverktyg är implementerade i programmeringsspråk i form av vissa syntaktiska medel som gör det möjligt att beskriva data (datatyper) och algoritmer (procedurer, funktioner, metoder) parametriserade av datatyper. För en funktion eller datatyp beskrivs formella typparametrar uttryckligen . Denna beskrivning är generaliserad och kan inte användas direkt i sin ursprungliga form.
På de platser i programmet där en generisk typ eller funktion används måste programmeraren uttryckligen ange den faktiska typparametern som anger deklarationen. Till exempel kan en generisk procedur för att byta två värden ha en typparameter som anger vilken typ av värden den byter. När programmeraren behöver byta två heltalsvärden anropar han proceduren med typparametern " heltal " och två parametrar - heltal, när två strängar - med typparametern " sträng " och två parametrar - strängar. När det gäller data kan en programmerare till exempel beskriva en generisk typ " lista " med en typparameter som anger vilken typ av värden som lagras i listan. Sedan, när man beskriver verkliga listor, måste programmeraren ange en generisk typ och en typparameter, och på så sätt erhålla vilken lista som helst med samma deklaration.
När en kompilator stöter på ett anrop till en generisk typ eller funktion, utför den de nödvändiga statiska typkontrollprocedurerna , utvärderar möjligheten för en given instansiering och, om den är positiv, genererar den kod som ersätter den faktiska typparametern i stället för den formella typparametern i den allmänna beskrivningen. För en framgångsrik användning av generiska beskrivningar måste naturligtvis de faktiska parametertyperna uppfylla vissa villkor. Om en generisk funktion jämför värden för en typparameter måste varje konkret typ som används i den stödja jämförelseoperationer, om den tilldelar värden av en typparameter till variabler måste den konkreta typen säkerställa korrekt tilldelning.
I C++ är generisk programmering baserad på konceptet med en "mall", betecknad med mallens nyckelord . Det används flitigt i C++ Standard Library (se STL ) såväl som tredjepartsbibliotek boost , Loki . Ett stort bidrag till framväxten av avancerade generiska programmeringsverktyg i C++ gjordes av Alexander Stepanov .
Som ett exempel, låt oss ge en mall (generalisering) av en funktion som returnerar det större värdet av två.
// Funktionsmall beskrivningsmall < typnamn T > T max ( T x , T y ) { om ( x < y ) returnera y ; annan returnera x ; } ... // Använder funktionen som ges av mallen int a = max ( 10 , 15 ); ... dubbel f = max ( 123,11 , 123,12 ); ...eller en mall (generalisering) av en länkad listklass:
mall < classT > _ klasslista _ { /* ... */ offentliga : void Lägg till ( const T & Element ); bool Hitta ( const T & Element ); /* ... */ };Haskell tillhandahåller generisk datatypsprogrammering. I följande exempel a , en parametertypvariabel.
datalista a = Noll | _ Nackdelar a ( Lista a ) längd :: Lista a -> Int längd Noll = 0 längd ( Nackdelar _ tl ) = 1 + längd tlRäkneexempel:
längd ( Nackdelar 1 ( Nackdelar 2 Noll )) == 2Java har tillhandahållit generika som är syntaktisk baserade på C++ sedan J2SE 5.0. Detta språk har generika eller "behållare av typ T" - en delmängd av generisk programmering.
På .NET- plattformen dök generiska programmeringsverktyg upp i version 2.0.
// Deklaration av en generisk klass. public class GenericList < T > { void Add ( T input ) { } } class TestGenericList { private class ExampleClass { } static void Main () { GenericList < int > list1 = new GenericList < int >(); GenericList < string > list2 = new GenericList < string >(); GenericList < ExampleClass > list3 = new GenericList < ExampleClass >(); } }Ett exempel på rekursiv generering baserat på D- mallar :
// http://digitalmars.com/d/2.0/template.html mall Foo ( T , R ...) // T är en typ, R är en uppsättning typer { void Foo ( T t , R r ) { skrivln ( t ); statisk if ( r . längd ) // om fler argument Foo ( r ); // gör resten av argumenten } } void main () { Foo ( 1 , 'a' , 6.8 ); } /++++++++++++++++ utskrifter: 1 a 6,8 +++++++++++++++/Stöd för generisk programmering av Free Pascal-kompilatorn har varit tillgängligt sedan version 2.2 2007 [7] . I Delphi - sedan oktober 2008 . Kärnstödet för generiska klasser dök upp först i Delphi 2007 .NET 2006 , men det påverkade bara .NET Framework . Mer komplett stöd för generisk programmering har lagts till i Delphi 2009 . Generiska klasser stöds också i Object Pascal i PascalABC.NET- systemet .