1)

Hieronder staat een python-functie summation, die kan de som van kubussen / vierkanten / .., soortgelijke bewerkingen uitvoeren.

def identity(k): return k def cube(k): return pow(k, 3) def square(k): return pow(k,2) def summation(n, term): if n == 0: return 0 else: return term(n) + summation(n-1, term) def sum_cubes(n): return summation(n, cube) if __name__ == "__main__": sum = sum_cubes(4) print(sum) """ In C, We can implement the same using function pointers. Goal is, to perform similar operations(Sum of ..) using single function summation()""" 

2)

Beschouw hieronder het sorteren van api van C,

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); 

Hier qsort kan gegevens van elk type sorteren , array van drijvers / bestandsnamen in een directory / strings / …


Vraag:

Hoe definieer je een generieke functie?

Is summation een generieke functie?

of

Is qsort een generieke functie?

of

Gegeven twee voorbeelden: is Generieke functie een ongeldige terminologie?

Opmerking: motivatie-tot-term qsort of een andere sorteerfunctie die ik ontwerp

Opmerkingen

  • Welke definitie van ” generieke functie ” heb je gelezen dat je ‘ niet begrijpt? Het zou helpen als je dat zou posten in plaats van een hoop code te schrijven.
  • De typetheoretische term voor het soort generiek waarbij een functie voor elk type werkt zonder beperking en zonder kennis van het specifieke type is parametrisch polymorfisme . De identiteitsfunctie is op deze manier generiek.
  • In sommige talen (zoals Java) heeft ” generieke functie ” een specifieke technische definitie. Maar dit is niet het geval in Python, dus ” generieke functie ” heeft geen goed gedefinieerde betekenis. Het betekent niet dat het ” ongeldige terminologie ” is, maar dat u zich bewust moet zijn van de context wanneer u de term gebruikt.
  • @AndresF. Javascrpt gebruikt deze generieke functie terminologie ook veel. Omdat je misschien een functie hebt die elk html-element nodig heeft om te verwerken (voorbeeld – verwijder alle onderliggende items van een bepaald html-element)

Answer

Er zijn verschillende betekenissen aan” algemeen “.

Informele definitie

“generiek” in alledaagse taal iets dat gemeenschappelijke eigenschappen deelt, maar in sommige opzichten minder specifiek is.

Vanuit dit perspectief zou je qsort() als algemeen kunnen beschouwen: de code van deze functie kan elke datastructuur met een vaste grootte sorteren waarvoor u een vergelijkingsfunctie kunt definiëren met behulp van het QSORT-algoritme.

Hetzelfde geldt voor uw summation() -functie, die termen samenvat die zijn verkregen met behulp van functies met één parameter.

Formele definitie

Programmeertalen zoals C ++ of Java maken generiek programmeren mogelijk met behulp van sjablonen of generieke gegevens:

Definitie van de C ++ 14-standaard : Een sjabloon definieert een familie van klassen of functies of een alias voor een familie van typen.

Het principe is dat de implementatie van een klasse of een functie kan worden geparametriseerd door typen.

Volgens dit meer formele standpunt, qsort() is geen generieke functie. De implementatie ervan hoeft bij de compilatie geen enkel type te bepalen en het gedrag is type-onafhankelijk. Het enige dat nodig is, is de grootte van de elementen die worden gesorteerd, en deze grootte is een gewoon argument dat tijdens runtime wordt verwerkt.

Voor een taal die niet statisch is getypt, zoals Python , “weet ik niet zeker wat ik moet antwoorden voor summation(). Ik denk dat het niet algemeen is omdat de implementatie en het gedrag ervan niet type-afhankelijk zijn: deze functie is slechts een functie van hogere orde, waarbij het argument term is een functie. Het gebruikt geen enkele functie die het gedrag van deze functie zou veranderen op basis van typen.

Ter illustratie van een generieke functie kun je de C ++ standaardfunctie bekijken std::sort() : zijn implementatie hangt af van het type van zijn argumenten (en optioneel een vergelijkingsfunctie met argumenten van een bepaald type). Door de functies van C ++ -sjablonen te gebruiken, kan het elke container van elk type sorteren, op voorwaarde dat het de operators / lidfuncties / eigenschappen / iteratoren heeft die vereist zijn voor de implementatie van de generieke functie.

Kan een dynamisch getypte taal generieke functies hebben

Dynamisch getypte taal vereist minder generieke code dan statisch getypte talen.

Als u bijvoorbeeld een container met objecten van een dynamisch type heeft, zou een qsort-functie de container generiek kunnen sorteren, zolang elke combinatie van twee elementen in de container kan worden vergeleken.

Maar zelfs in zon flexibele omgeving kan generieke –type afhankelijke – programmering nuttig zijn. De typische use case zijn multimethoden, waarbij het gedrag of de code afhangt van het type argumenten of zelfs de combinatie van typen (zoals voor het bepalen van de kruising tussen twee verschillende vormen). Zie voor meer informatie:

Reacties

  • Niet zeker, waarom vergelijken we Generics (voornamelijk gebruikt om type-casting in Java te vermijden & helpt bij het uitvoeren van polymorfisme) met definitie van generieke functie?
  • @overexchange Ik denk dat java ook generiek programmeren biedt, inclusief generieke methoden (zie specificatie of tutorial ). Desalniettemin heb ik ‘ het definitiegedeelte enigszins aangepast om uw opmerking te beantwoorden.
  • Het generieke pakket van Python heeft niets te maken met generieke functies. Behalve dat ze hetzelfde bijvoeglijk naamwoord delen.
  • @Killian if generiek programmeren , gaat over het idee van abstraheren van concrete, efficiënte algoritmen om generieke algoritmen te verkrijgen die kunnen worden gecombineerd met verschillende dataweergaven , denk ik dat de multimethoden in dat pakket zouden moeten zijn, don ‘ denk je van niet?

Answer

Generieke functies nemen tijdens het compileren generiek het type aan van ten minste één functieargument. Dat wil zeggen, de compiler zoekt uit welk type op een bepaalde plaats wordt gebruikt en past precies dit type toe waar het in de functie wordt gebruikt. Bijv. als je een algemeen argument in je functie hebt dat wordt gebruikt met een + operator, moet het type de juiste methoden hebben. Voor strings / arrays zou dit in veel gevallen een aaneenschakeling zijn en voor en integer / float een optelling. De compiler kan detecteren dat en de juiste bewerking toepassen. Je C-routine is in die zin niet generiek, aangezien het de programmeur is die informatie over de grootte toepast en niet de compiler die het type detecteert en de juiste grootte gebruikt.

Bijvoorbeeld in een fictieve taal

func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3 

Hier detecteert de compiler in het eerste geval dat twee strings worden toegepast en zal intern iets uitbreiden als

func add(p1:string, p2:string) 

en behandel de + als aaneenschakeling, terwijl deze in het tweede geval zou uitbreiden

func add(p1:int, p2:int) 

zoals geleverd integer parameters. Generiek betekent dat de compiler individuele code genereert tijdens het compileren. Python is bijvoorbeeld niet getypeerd en zou dat soort substitutie doen tijdens runtime. Betekent: Python heeft geen generieke functies omdat alles een beetje generiek is.

Opmerkingen

  • Begrijp je idee niet. je bedoelt + is een generieke functie, syntaxis in C ++?
  • Functie-aanname-functieargumenten zijn van hogere orde functie s in Python / JavaScript-wereld. In C hebben we functie-aanwijzers nodig, voor hetzelfde.
  • Zie mijn bewerking hierboven.
  • Dus, wat voor soort functie is summation, Hogere orde functie? en niets meer dan dat?
  • Er zijn veel definities van wat generiek is. Stroustrup definieert het bijvoorbeeld als ” programmering met behulp van typen als parameters “. Voor de Wikipedia-referentie ga ik ‘ liever naar: en.wikipedia.org/wiki/Generic_programming

Answer

Ik ga dit starten vanuit het perspectief van C ++, en dan mijn weg naar C.

In statisch getypeerde talen zoals C, C ++, Java, enz., stelt een “generieke” functie je in staat om de functiebewerkingen een keer te specificeren, door plaatshouders te gebruiken voor elk type dat kan variëren tussen verschillende aanroepen (wat betekent dat functies als qsort en bsearch zeer zeker niet generieke functies). Idealiter zou je “ook willen dat de compiler automatisch alle aanroepen van deze generieke functie detecteert en de eigenlijke code genereert als dat nodig is.

C ++ maakt dit 1 gemakkelijk door sjablonen aan te bieden:

template <typename T> T summation( T *values, size_t numValues ) { T result = 0; for ( size_t i = 0; i < numValues; i++ ) result += values[i]; return result; } 

T is een tijdelijke aanduiding voor elk type 2 , dus je kunt het noemen als

int ivals[] = {1,2,3,4,5,6,7,8,9}; double dvals[] = {1,2,3,4,5,6,7,8,9}; int sumi = summation( ivals, 10 ); double sumd = summation( dvals, 10 ); 

Wanneer de code is gecompileerd, ziet de compiler de twee aanroepen van summation en leidt de typen argumenten af. Voor elk verschillend type, genereert het genereert een nieuwe instantie van de functie, waardoor het een unieke naam krijgt:

int summation_i( int *values, size_t numValues ) // actual compilers will generate { // more complex "mangled" names int result = 0; // than this ... } double summation_d( double *values, size_t numValues ) { double result = 0; ... } 

Het genereert vervolgens code zodat het resultaat van summation_i is toegewezen aan sumi en summation_d is toegewezen aan sumd.

C biedt niets vergelijkbaars met de sjabloonfunctie. Traditioneel hebben we “generieke programmering op twee manieren aangevallen – ofwel door macros te gebruiken of door void * overal en typebewuste bewerkingen delegeren aan andere functies.

Hier “een slecht voorbeeld van een macrogebaseerde oplossing:

#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(t,x,s) summation_##t(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(int, ivals, 5); double sumd = SUMMATION(double, dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; } 

De SUMMATION_DEF is ruwweg vergelijkbaar met een sjabloon in die zin dat het de functiebewerkingen specificeert, met de macroparameter t als tijdelijke aanduiding voor het type. We gebruiken ook t als onderdeel van de functienaam – de ## is de operator voor het plakken van tokens, en de preprocessor zal t en voeg die waarde toe aan de naam van de functie 3 .

Waar het verschilt van C ++ is het feit dat een macro slechts een domme tekstvervanging is. Het triggert niet eventuele speciale bewerkingen van de kant van de compiler. De feitelijke functie-instanties worden “niet automatisch gegenereerd op basis van aanroepen van de SUMMATION macro – we moeten expliciet de functies genereren die we willen (vandaar de SUMMATION_DEF(int) en SUMMATION_DEF(double) vóór main). Het betekent ook dat wanneer we summation_xxx via de SUMMATION macro, moeten we het type doorgeven als onderdeel van de macro-argumentlijst, zodat de juiste functie wordt aangeroepen. Wat een pijn.

De C 2011-standaard heeft het trefwoord _Generic toegevoegd, wat het leven in dat opzicht een beetje gemakkelijker kan maken:

#include <stdio.h> #define SUMMATION_DEF(t) \ t summation_##t( t *values, size_t numValues ) \ { \ t result = 0; \ for ( size_t i = 0; i < numValues; i++ ) \ result += values[i]; \ return result; \ } #define SUMMATION(x,s) _Generic((x), \ int * : summation_int, \ double * : summation_double \ )(x, s) SUMMATION_DEF(int) SUMMATION_DEF(double) int main( void ) { int ivals[] = {1, 2, 3, 4, 5}; double dvals[] = {1, 2, 3, 4, 5}; int sumi = SUMMATION(ivals, 5); double sumd = SUMMATION(dvals, 5); printf( "sumi = %d\n", sumi ); printf( "sumd = %f\n", sumd ); return 0; } 

De Met _Generic sleutelwoord kunt u uitdrukkingen evalueren op basis van typen ; dus als het type van het eerste argument naar SUMMATION is int *, we noemen summation_int; het is “s het” s double *, noemen we summation_double. Op deze manier hoeven we de typenaam niet op te geven in de macro-argumenten.

De andere benadering, zoals je “hebt gezien, is om void * te gebruiken en typebewuste bewerkingen te delegeren aan andere functies. Zoals ik hierboven al zei, dat” Het is niet echt “generiek” programmeren, aangezien je elke vergelijkingsfunctie voor elk type handmatig moet implementeren. Je kunt het niet één keer coderen en klaar zijn. En door void * te gebruiken, gooi je in feite type veiligheid uit het raam en in tegenliggers.

En voordat iemand klaagt – nee, geen van deze sommatiefuncties controleert of behandelt rekenkundige overloop. Dat is een onderwerp voor een andere dag.


  1. Voor voldoende losse definities van “gemakkelijk”. De metaprogrammeertaal die wordt gebruikt om sjablonen te ondersteunen, is Turing-compleet, dus je kunt er * echt geweldig * mee doen en onmogelijk dingen ermee te begrijpen.
  2. Voor voldoende losse definities van “elk type”. Merk op dat welk type je ook gebruikt de += operator moet ondersteunen, anders zal de compiler tegen je schreeuwen.
  3. Deze code wordt afgebroken voor typen als unsigned int of long double aangezien ze spaties in de naam hebben. Ik weet niet meteen de oplossing voor dat probleem, en ik heb nu al genoeg tijd aan dit antwoord besteed.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *