1)
Nedenfor er en python-funktion summation
kan udføre summen af terninger / firkanter / .., lignende operationer .
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)
Overvej nedenfor at sortere api fra C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Her kan qsort
sortere data af enhver type , array of floats / file names in a directory / strings / …
Spørgsmål:
Hvordan defineres en generisk funktion?
Er summation
en generisk funktion?
eller
Er qsort
en generisk funktion?
eller
Gives to eksempler, er generisk funktion en ugyldig terminologi?
Bemærk: Motivation til term qsort
eller en hvilken som helst slags funktion, som jeg designer
Kommentarer
- Hvilken definition af ” generisk funktion ” har du læst, at du ikke forstår ‘? Det ville hjælpe, hvis du skrev det i stedet for at skrive en masse kode.
- Typeteoriudtrykket for den slags genericisme, hvor en funktion fungerer for enhver type uden begrænsning og uden viden om den specifikke type er parametrisk polymorfisme . Identitetsfunktionen er generisk på denne måde.
- På nogle sprog (som Java) har ” generisk funktion ” en specifik teknisk definition. Men dette er ikke tilfældet i Python, så ” generisk funktion ” har ikke veldefineret betydning. Det betyder ikke, at det er ” ugyldig terminologi “, bare at du skal være opmærksom på konteksten, når du bruger udtrykket.
- @AndresF. Javascrpt bruger også denne generiske funktion terminologi meget. Fordi du muligvis har en funktion, der tager ethvert html-element til at behandle (eksempel – slet alle børn af det givne html-element)
Svar
Der er flere betydninger af” generisk “.
Uformel definition
“generisk” i hverdagssproget noget, der deler fælles egenskaber, men som er mindre specifikt på nogle måder.
Under dette perspektiv kan du overveje qsort()
som generisk: koden for denne funktion er i stand til at sortere en hvilken som helst datastruktur med fast størrelse, som du kan definere en sammenligningsfunktion for ved hjælp af QSORT-algoritmen.
Det samme gælder for din summation()
-funktion, som opsummerer udtryk opnået ved hjælp af alle funktioner med en parameter.
Formel definition
Programmeringssprog som C ++ eller Java tillader generisk programmering ved brug af skabeloner eller generiske oplysninger:
Definition fra C ++ 14-standarden : En skabelon definerer en familie af klasser eller funktioner eller et alias for en familie af typer.
Princippet er, at en klasse eller en funktion “implementering kan parametriseres efter typer.
Ifølge dette mere formelle synspunkt, qsort()
er ikke en generisk funktion. Dens implementering behøver ikke at bestemme nogen type ved kompilering, og dens adfærd er typeuafhængig. Det eneste, det har brug for, er størrelsen på de elementer, der sorteres, og denne størrelse er et almindeligt argument, der behandles ved kørselstid.
For et sprog, der ikke er statisk skrevet såsom Python , er jeg ikke sikker på hvad jeg skal svare på summation()
. Jeg synes, det er ikke generisk, fordi dets implementering og opførsel ikke er typeafhængig: denne funktion er bare en funktion af højere orden, med argumentet term
en funktion. Den bruger ikke nogen funktion, der kan ændre funktionsmåden for denne funktion baseret på typer.
For at illustrere en generisk funktion kan du se på C ++ standardfunktionen std::sort()
: dens implementering afhænger af typen af argumenter (og eventuelt en sammenligningsfunktion med argumenter af en bestemt type). Ved at bruge funktionerne i C ++ skabeloner kan den sortere enhver beholder af enhver type under den betingelse, at den har de operatører / medlemsfunktioner / træk / iteratorer, der kræves ved implementeringen af den generiske funktion.
Kan et dynamisk skrevet sprog have generiske funktioner
Dynamisk skrevet sprog kræver mindre generisk kode end statisk typede sprog.
For eksempel, hvis du har en beholder med objekter af dynamisk type, kan en qsort-funktion generisk sortere beholderen, så længe enhver kombination af to elementer i beholderen kan sammenlignes.
Men selv i et så fleksibelt miljø kan generisk –typeafhængig – programmering være nyttigt. Den typiske brugssag er multimetoder, hvor adfærd eller kode afhænger af typen af argumenter eller endda kombinationen af typer (f.eks. Til bestemmelse af skæringspunktet mellem to forskellige former). For yderligere information se:
- Generisk programmeringspakke til python: multidispatching
- Gør generiske funktioner anvendelige i smalltalk
- Eksempel på generisk funktion i Common Lisp
Kommentarer
- Ikke sikker, hvorfor sammenligner vi Generics (hovedsageligt brugt til at undgå type casting i Java & hjælper med at udføre polymorfisme) med definition af generisk funktion?
- @overexchange Jeg tror, at java også tilbyder generisk programmering, inklusive generiske metoder (se specifikation eller tutorial ). Ikke desto mindre har jeg ‘ redigeret lidt definitionsdelen for at adressere din bemærkning.
- Den generiske pakke fra Python har intet at gøre med generiske funktioner. Bortset fra at de deler det samme adjektiv.
- @Killian hvis generisk programmering handler om ideen om at abstrahere fra konkrete, effektive algoritmer for at opnå generiske algoritmer, der kan kombineres med forskellige datarepræsentationer , tror jeg, at multimetoderne i den pakke skal være i, ikke ‘ t du tror det?
Svar
Generiske funktioner tager typen af mindst et funktionsargument generelt ved kompileringstidspunktet. Det vil sige, compileren finder ud af, hvilken type der bruges på et bestemt sted og anvender nøjagtigt denne type, hvor den bruges i funktionen. For eksempel. hvis du har et generisk argument i din funktion, der bruges med en +
-operator, skal typen have passende metoder. For strenge / arrays ville dette i mange tilfælde være en sammenkædning og for og heltal / float en tilføjelse. Compileren kan registrere, at en anvender den korrekte operation. Din C-rutine er ikke generisk i den forstand, da det er programmøren, der anvender nogle størrelsesoplysninger og ikke kompilatoren registrerer typen og bruger den rigtige størrelse.
F.eks. På et fiktivt sprog
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Her registrerer compileren i det første tilfælde, at der anvendes to strenge og internt udvider noget som
func add(p1:string, p2:string)
og behandle +
som sammenkædning, mens det i det andet tilfælde ville udvides
func add(p1:int, p2:int)
som leveret heltalsparametre. Generisk betyder, at kompilatoren genererer individuel kode under kompileringstid. Python er for eksempel ikke skrevet og ville gøre den slags erstatning under kørsel. Betydning: Python har ikke generiske funktioner, da alt er slags generisk.
Kommentarer
- Fik ikke din idé. du mener + er en generisk funktion, syntaks i C ++?
- Argumenter for funktionstagende funktion er højere orden fungere s i Python / JavaScript-verden. I C har vi brug for funktionsmarkører til samme.
- Se min redigering ovenfor.
- Så hvilken slags funktion er
summation
, Funktion med højere ordre? og intet mere end det? - Der er mange definitioner for, hvad generisk er. Stroustrup definerer for eksempel det som ” programmering ved hjælp af typer som parametre “. Til wikipedia-henvisningen går jeg ‘ hellere til: da.wikipedia.org/wiki/Generic_programming
Svar
Jeg vil starte dette ud fra C ++ perspektivet og derefter arbejde mig ind i C.
På statisk typede sprog som C, C ++, Java osv. giver en “generisk” funktion dig mulighed for at specificere funktionshandlingerne en gang ved hjælp af pladsholdere til alle typer, der kan variere mellem forskellige opkald (hvilket betyder at funktioner som qsort
og bsearch
er absolut ikke generiske funktioner). Ideelt set vil du også gerne have, at compileren automatisk registrerer opkald til denne generiske funktion og genererer den aktuelle kode efter behov.
C ++ gør dette let 1 ved at tilbyde skabeloner :
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
er en pladsholder for enhver type 2 , så du kan kalde det som
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 );
Når koden er kompileret, ser kompilatoren de to opkald til summation
og educerer typerne af argumenterne. For hver anden type enererer den en ny forekomst af funktionen, der giver den et unikt navn:
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; ... }
Den genererer derefter kode sådan at resultatet af summation_i
tildeles sumi
og summation_d
tildeles sumd
.
C tilbyder ikke noget, der ligner skabelonfunktionen. Traditionelt har vi angrebet generisk programmering på en af to måder – enten ved hjælp af makroer eller ved hjælp af void *
overalt og delegere typebevidste operationer til andre funktioner.
Her er et dårligt eksempel på en makrobaseret løsning:
#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; }
SUMMATION_DEF
svarer stort set til en skabelon, idet den specificerer funktionshandlingerne ved hjælp af makroparameteren t
som en type pladsholder. Vi bruger også t
som en del af funktionsnavnet – ##
er token-indsættelsesoperatoren, og forprocessoren udvider t
og tilføj denne værdi til navnet på funktionen 3 .
Hvor den adskiller sig fra C ++, er det faktum, at en makro bare er en stum tekstudskiftning. Det udløser ikke eventuelle specielle operationer fra kompilatorens side. De faktiske funktionsforekomster genereres ikke automatisk baseret på eventuelle påkaldelser af SUMMATION
makroen – vi skal udtrykkeligt generere de funktioner, vi ønsker (dermed SUMMATION_DEF(int)
og SUMMATION_DEF(double)
før main
). Det betyder også, at når vi kalder summation_xxx
gennem SUMMATION
makroen skal vi videregive typen som en del af makroargumentlisten, så den rigtige funktion bliver kaldt. Hvilken smerte.
C 2011-standard tilføjede _Generic
nøgleordet, hvilket kan gøre livet lidt lettere i den henseende:
#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; }
The _Generic
nøgleord giver dig mulighed for at evaluere udtryk baseret på typer ; hvis typen af det første argument til SUMMATION
er int *
, vi kalder summation_int
; det “s det” s double *
, vi kalder summation_double
. På denne måde behøver vi ikke angive typenavnet i makroargumenterne.
Den anden tilgang, som du har set, er at bruge void *
og at delegere typebevidste operationer til andre funktioner. Som jeg sagde ovenfor, at ” s egentlig ikke “generisk” programmering, da du skal implementere hver sammenligningsfunktion manuelt for hver type. Du kan ikke bare kode det en gang og være færdig med det. Og ved at bruge void *
kaster du dybest set typesikkerhed ud af vinduet og ind i modkørende trafik.
Og før nogen klager – nej, ingen af disse summeringsfunktioner kontrollerer eller behandler aritmetisk overløb. Det er et emne en anden dag.
- For tilstrækkeligt løse definitioner af “let”. Metaprogrammeringssproget, der bruges til at understøtte skabeloner, er Turing-komplet, så du kan gøre * virkelig fantastisk * og umuligt at forstå ting med det.
- For tilstrækkeligt løse definitioner af “enhver type”. Bemærk, at uanset hvilken type du bruger skal understøtte
+=
operatøren, ellers råber compileren dig. - Denne kode brydes for typer som
unsigned int
ellerlong double
, da de har et hvidt mellemrum i navnet. Jeg kender ikke straks løsningen på dette problem, og jeg har brugt nok tid på dette svar, som det er.