Generisk programmering er et programmeringsparadigme , der består i en sådan beskrivelse af data og algoritmer , der kan anvendes på forskellige typer data uden at ændre selve beskrivelsen. I en eller anden form understøttes det af forskellige programmeringssprog . Generiske programmeringsfunktioner dukkede først op i form af generiske funktioner (generiske funktioner) i 1970'erne i Clu- og Ada -sprogene , derefter som parametrisk polymorfi i ML og dets efterkommere, og derefter i mange objektorienterede sprog som C++ , Python [ 1] , Java , Object Pascal [2] , D , Eiffel , sprog til .NET -platformen og andre.
Generisk programmering betragtes som en programmeringsmetodologi baseret på adskillelse af datastrukturer og algoritmer gennem brug af abstrakte kravbeskrivelser [3] . Abstrakte kravdeklarationer er en udvidelse af konceptet med en abstrakt datatype . I stedet for at beskrive en enkelt type i generisk programmering, bruges en beskrivelse af en familie af typer, der har en fælles grænseflade og semantisk adfærd . Et sæt krav, der beskriver en grænseflade og semantisk adfærd , kaldes et koncept . Således kan en algoritme skrevet i en generaliseret stil anvendes på enhver type, der tilfredsstiller den med dens koncepter. Denne mulighed kaldes polymorfi .
En type siges at modellere et koncept (er en model af et koncept), hvis den opfylder dets krav. Et koncept er en forfining af et andet koncept, hvis det supplerer sidstnævnte. Konceptkravene indeholder følgende oplysninger: [4]
I C++ implementeres OOP gennem virtuelle funktioner og nedarvning, mens OP (generisk programmering) implementeres gennem klasse- og funktionsskabeloner. Imidlertid er essensen af begge metoder kun indirekte relateret til specifikke implementeringsteknologier. Mere formelt er OOP baseret på subtype polymorfi , mens OP er baseret på parametrisk polymorfi . På andre sprog kan begge implementeres forskelligt. For eksempel har multimetoder i CLOS semantik svarende til parametrisk polymorfi.
Masser og Stepanov skelner mellem følgende stadier i løsningen af problemet i henhold til OP-metoden:
Minimering og framing sigter mod at skabe en struktur, således at algoritmerne er uafhængige af specifikke datatyper. Denne tilgang afspejles i strukturen af STL - biblioteket . [5]
En alternativ tilgang til at definere generisk programmering , som kan kaldes generisk datatypeprogrammering , blev foreslået af Richard Bird og Lambert Meertens . I den er datatypestrukturer parametre for generiske programmer. For at gøre dette introduceres et nyt abstraktionsniveau i programmeringssproget, nemlig parametrisering med hensyn til klasser af algebraer med en variabel signatur . Selvom teorierne for begge tilgange er uafhængige af programmeringssproget, har Musser-Stepanov tilgangen, som lægger vægt på konceptanalyse, gjort C++ til sin hovedplatform, mens generisk datatypeprogrammering næsten udelukkende bruges af Haskell og dens varianter [6] .
Generiske programmeringsværktøjer er implementeret i programmeringssprog i form af visse syntaktiske midler, der gør det muligt at beskrive data (datatyper) og algoritmer (procedurer, funktioner, metoder) parametriseret af datatyper. For en funktion eller datatype er formelle typeparametre eksplicit beskrevet . Denne beskrivelse er generaliseret og kan ikke bruges direkte i sin oprindelige form.
De steder i programmet, hvor der anvendes en generisk type eller funktion, skal programmøren eksplicit angive den faktiske typeparameter, der angiver deklarationen. For eksempel kan en generisk procedure til at bytte to værdier have en typeparameter, der specificerer den type værdier, den bytter. Når programmøren skal bytte to heltalsværdier, kalder han proceduren med typeparameteren " heltal " og to parametre - heltal, når to strenge - med typeparameteren " streng " og to parametre - strenge. I tilfælde af data kan en programmør for eksempel beskrive en generisk type " liste " med en typeparameter, der specificerer typen af værdier, der er gemt i listen. Når programmøren derefter beskriver reelle lister, skal den specificere en generisk type og en typeparameter, og dermed opnå enhver ønsket liste ved hjælp af den samme erklæring.
Når en compiler støder på et kald til en generisk type eller funktion, udfører den de nødvendige statiske typekontrolprocedurer , evaluerer muligheden for en given instansiering og, hvis den er positiv, genererer den kode, der erstatter den faktiske typeparameter i stedet for den formelle typeparameter i den generiske beskrivelse. For en vellykket brug af generiske beskrivelser skal de faktiske parametertyper naturligvis opfylde visse betingelser. Hvis en generisk funktion sammenligner værdier af en typeparameter, skal enhver konkret type, der anvendes i den, understøtte sammenligningsoperationer, hvis den tildeler værdier af en typeparameter til variabler, skal den konkrete type sikre korrekt tildeling.
I C++ er generisk programmering baseret på konceptet om en " skabelon ", angivet med skabelonnøgleordet . Det er meget udbredt i C++ Standard Library (se STL ) såvel som tredjeparts biblioteker boost , Loki . Et stort bidrag til fremkomsten af avancerede generiske programmeringsværktøjer i C++ blev givet af Alexander Stepanov .
Lad os som et eksempel give en skabelon (generalisering) af en funktion, der returnerer den største værdi af to.
// Funktionsskabelon beskrivelse skabelon < typenavn T > T max ( T x , T y ) { hvis ( x < y ) returnere y ; andet returnere x ; } ... // Anvendelse af funktionen givet af skabelonen int a = max ( 10 , 15 ); ... dobbelt f = max ( 123,11 , 123,12 ); ...eller en skabelon (generalisering) af en linket listeklasse:
skabelon < classT > _ klasseliste _ { /* ... */ offentligt : void Tilføj ( const T & Element ); bool Find ( const T & Element ); /* ... */ };Haskell leverer generisk datatypeprogrammering. I det følgende eksempel a , en parametertypevariabel.
dataliste a = Nul | _ Ulemper a ( Liste a ) længde :: Liste a -> Int længde Nul = 0 længde ( Cons _ tl ) = 1 + længde tlRegneeksempel:
længde ( Ulemper 1 ( Ulemper 2 Nul )) == 2Java har leveret generiske artikler, der er syntaktisk baseret på C++ siden J2SE 5.0. Dette sprog har generiske eller "beholdere af type T" - en delmængde af generisk programmering.
På .NET -platformen dukkede generiske programmeringsværktøjer op i version 2.0.
// Erklæring om en generisk klasse. public class GenericList < T > { void Tilføj ( T input ) { } } class TestGenericList { private class ExampleClass { } static void Main () { GenericList < int > list1 = new GenericList < int >(); GenericList < string > list2 = ny GenericList < string >(); GenericList < ExampleClass > list3 = new GenericList < ExampleClass >(); } }Et eksempel på rekursiv generering baseret på D- skabeloner :
// http://digitalmars.com/d/2.0/template.html skabelon Foo ( T , R ...) // T er en type, R er et sæt typer { void Foo ( T t , R r ) { skrivln ( t ); statisk if ( r . længde ) // hvis flere argumenter Foo ( r ); // gør resten af argumenterne } } void main () { Foo ( 1 , 'a' , 6.8 ); } /++++++++++++++++ udskrifter: 1 a 6,8 +++++++++++++++/Understøttelse af generisk programmering af Free Pascal-kompileren har været tilgængelig siden version 2.2 i 2007 [7] . I Delphi - siden oktober 2008 . Kerneunderstøttelsen af generiske klasser dukkede først op i Delphi 2007 .NET i 2006 , men den påvirkede kun .NET Framework . Mere komplet support til generisk programmering er blevet tilføjet i Delphi 2009 . Generiske klasser understøttes også i Object Pascal i PascalABC.NET -systemet .