1)
Nedenfor er en pythonfunksjon summation
, at kan utføre summen av kuber / firkanter / .., lignende operasjoner .
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)
Vurder, under sortering av api fra C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Her kan qsort
sortere data av hvilken som helst type , array of floats / file names in a directory / strings / …
Spørsmål:
Hvordan definere en generisk funksjon?
Er summation
en generisk funksjon?
eller
Er qsort
en generisk funksjon?
eller
Gitt to eksempler, er generisk funksjon en ugyldig terminologi?
Merk: Motivasjon-til-term qsort
eller en hvilken som helst slags funksjon som jeg designer
Kommentarer
- Hvilken definisjon av » generisk funksjon » har du lest at du ikke forstår ‘? Det ville hjelpe hvis du la ut det i stedet for å skrive en haug med kode.
- Typeteorietermet for den typen generisme der en funksjon fungerer for alle typer uten begrensninger og uten kunnskap om den spesifikke typen er parametrisk polymorfisme . Identitetsfunksjonen er generisk på denne måten.
- På noen språk (som Java) har » generisk funksjon » en spesifikk teknisk definisjon. Men dette er ikke tilfelle i Python, så » generisk funksjon » har ikke veldefinert betydning. Det betyr ikke at det er » ugyldig terminologi «, bare at du skal være klar over konteksten når du bruker begrepet.
- @AndresF. Javascrpt bruker også denne generiske funksjonen terminologien mye. Fordi du kan ha en funksjon som tar ethvert html-element å behandle (eksempel – slett alle barn av gitt html-element)
Svar
Det er flere betydninger av» generisk «.
Uformell definisjon
«generisk» i hverdagsspråk noe som deler vanlige egenskaper, men som er mindre spesifikk på noen måter.
Under dette perspektivet kan du vurdere qsort()
som generisk: koden til denne funksjonen er i stand til å sortere en hvilken som helst datastruktur med fast størrelse som du kan definere en sammenligningsfunksjon for ved hjelp av QSORT-algoritmen.
Det samme gjelder summation()
-funksjonen din, som oppsummerer vilkår som er oppnådd ved hjelp av funksjoner med en parameter.
Formell definisjon
Programmeringsspråk som C ++ eller Java tillater generisk programmering med bruk av maler eller generikk:
Definisjon fra C ++ 14-standarden : En mal definerer en familie av klasser eller funksjoner eller et alias for en familie av typer.
Prinsippet er at en klasse eller en funksjons implementering kan parametriseres etter typer.
I følge dette mer formelle synspunktet, qsort()
er ikke en generisk funksjon. Implementeringen trenger ikke å bestemme hvilken som helst type ved kompilering, og dens oppførsel er typeuavhengig. Det eneste det trenger, er størrelsen på elementene som blir sortert, og denne størrelsen er et vanlig argument som behandles i løpetid.
For et språk som ikke er statisk skrevet, for eksempel Python , er jeg ikke sikker på hva jeg skal svare på summation()
. Jeg tror det ikke er generisk fordi implementeringen og oppførselen ikke er typeavhengig: denne funksjonen er bare en funksjon av høyere orden, med argumentet term
en funksjon. Den bruker ikke noen funksjon som kan endre oppførselen til denne funksjonen basert på typer.
For illustrasjon av en generisk funksjon, kan du ta en titt på C ++ standardfunksjon std::sort()
: implementeringen avhenger av typen argumenter (og eventuelt en sammenligningsfunksjon med argumenter av en bestemt type). Ved å bruke funksjonene til C ++ maler kan den sortere hvilken som helst beholder av hvilken som helst type, under forutsetning av at den har operatørene / medlemsfunksjonene / trekkene / iteratorene som kreves av implementeringen av den generiske funksjonen.
Kan et dynamisk skrevet språk ha generiske funksjoner
Dynamisk skrevet språk krever mindre generisk kode enn statisk typede språk.
Hvis du for eksempel har en beholder med objekter av dynamisk type, kan en qsort-funksjon generisk sortere beholderen, så lenge en kombinasjon av to elementer i beholderen kan sammenlignes.
Men selv i et så fleksibelt miljø kan generisk –typeavhengig – programmering være nyttig. Den typiske brukssaken er multimetoder, hvor oppførselen eller koden avhenger av typen argumenter eller til og med kombinasjonen av typer (for eksempel for å bestemme skjæringspunktet mellom to forskjellige former). For ytterligere informasjon se:
- Generisk programmeringspakke for python: multidispatching
- Gjør generiske funksjoner brukbare i smalltalk
- Eksempel på generisk funksjon i Common Lisp
Kommentarer
- Ikke sikker, hvorfor sammenligner vi Generics (hovedsakelig brukt for å unngå type casting i Java & hjelper med å utføre polymorfisme) med definisjon av generisk funksjon?
- @overexchange Jeg tror at java også tilbyr generisk programmering, inkludert generiske metoder (se spesifikasjon eller tutorial ). Ikke desto mindre har jeg ‘ redigert definisjonsdelen litt for å adressere din kommentar.
- Generic-pakken fra Python har ingenting å gjøre med generiske funksjoner. Med unntak av at de har samme adjektiv.
- @Killian hvis generisk programmering , handler om ideen om å abstrahere fra konkrete, effektive algoritmer. for å få generiske algoritmer som kan kombineres med forskjellige datarepresentasjoner , tror jeg at multimetodene i den pakken skal være i, ikke ‘ t du tror det?
Svar
Generiske funksjoner tar typen av minst ett funksjonsargument generisk ved kompileringstidspunktet. Det vil si at kompilatoren finner ut hvilken type som brukes på et bestemt sted og bruker nøyaktig denne typen der den brukes i funksjonen. F.eks. hvis du har et generelt argument i funksjonen din som brukes med en +
-operatør, må typen ha passende metoder. For strenger / arrays vil dette i mange tilfeller være en sammenkobling og for og heltall / flyte et tillegg. Kompilatoren kan oppdage at en bruker riktig operasjon. C-rutinen din er ikke generisk i den forstand, siden det er programmereren som bruker litt størrelsesinformasjon og ikke kompilatoren som oppdager typen og bruker riktig størrelse.
F.eks. På noe fiktivt språk
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Her oppdager kompilatoren i det første tilfellet at to strenger brukes og vil utvide noe sånt som
func add(p1:string, p2:string)
og behandle +
som sammenkobling, mens det i det andre tilfellet vil utvides
func add(p1:int, p2:int)
som levert heltallsparametere. Generisk betyr at kompilatoren genererer individuell kode i løpet av kompileringstiden. Python er for eksempel ikke skrevet og vil gjøre den typen erstatning under kjøretiden. Betyr: Python har ikke generiske funksjoner siden alt er slags generisk.
Kommentarer
- Fikk ikke ideen din. du mener + er en generisk funksjon, syntaksen i C ++?
- Argumenter for funksjonsfunksjonen er høyere orden funksjon s i Python / JavaScript-verden. I C trenger vi funksjonspekere for det samme.
- Se min redigering ovenfor.
- Så hva slags funksjon er
summation
, Høyere ordrefunksjon? og ikke noe mer enn det? - Det er mange definisjoner for hva generisk er. Stroustrup definerer for eksempel det som » programmering ved hjelp av typer som parametere «. For wikipedia-referansen går jeg ‘ heller til: en.wikipedia.org/wiki/Generic_programming
Svar
Jeg skal starte dette fra perspektivet til C ++, og deretter jobbe meg inn i C.
På statisktypede språk som C, C ++, Java, etc., lar en «generisk» funksjon deg spesifisere funksjonsoperasjonene en gang ved å bruke plassholdere for alle typer som kan variere mellom forskjellige samtaler (som betyr at funksjoner som qsort
og bsearch
er absolutt ikke generiske funksjoner). Ideelt sett vil du også at kompilatoren automatisk skal oppdage samtaler til denne generiske funksjonen og generere den faktiske koden etter behov.
C ++ gjør dette enkelt 1 ved å tilby maler :
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 plassholder for alle typer 2 , så du kan kalle 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 kompilert, ser kompilatoren de to anropene til summation
og educerer typene av argumentene. For hver annen type genererer den en ny forekomst av funksjonen, og gir 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 deretter kode slik at resultatet av summation_i
er tildelt sumi
og summation_d
tildeles sumd
.
C tilbyr ikke noe som ligner på malfunksjonen. Tradisjonelt har vi angrepet generisk programmering på en av to måter – enten ved å bruke makroer eller ved å bruke void *
overalt og delegere typebevisste operasjoner til andre funksjoner.
Her er et dårlig eksempel på en makrobasert 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
er omtrent lik en mal ved at den spesifiserer funksjonsoperasjonene, ved hjelp av makroparameteren t
som en plassholder. Vi bruker også t
som en del av funksjonsnavnet – ##
er symbolet for liming av token, og forprosessoren vil utvide t
og legg den verdien til navnet på funksjonen 3 .
Hvor den skiller seg fra C ++, er det faktum at en makro bare er en dum tekstutskiftning. eventuelle spesielle operasjoner fra kompilatorens side. De faktiske funksjonsforekomstene genereres ikke automatisk basert på alle påkallelser av SUMMATION
makroen – vi må eksplisitt generere de funksjonene vi ønsker (derav SUMMATION_DEF(int)
og SUMMATION_DEF(double)
før main
). Det betyr også at når vi kaller summation_xxx
gjennom SUMMATION
makroen, må vi passere typen som en del av listen over makroargumenter, slik at den rette funksjonen blir kalt. For en smerte.
C 2011-standarden la til _Generic
søkeordet, noe som kan gjøre livet litt lettere i så måte:
#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økkelord lar deg evaluere uttrykk basert på typer ; dermed hvis typen av det første argumentet til SUMMATION
er int *
, vi kaller summation_int
; det «s it» s double *
, vi kaller summation_double
. På denne måten trenger vi ikke å spesifisere typenavnet i makroargumentene.
Den andre tilnærmingen, som du har sett, er å bruke void *
og å delegere typebevisste operasjoner til andre funksjoner. Som jeg sa ovenfor, at » s egentlig ikke «generisk» programmering, siden du må implementere hver sammenligningsfunksjon manuelt for hver type. Du kan ikke bare kode det en gang og være ferdig med det. Og ved å bruke void *
kaster du i utgangspunktet typesikkerhet ut av vinduet og inn i møtende trafikk.
Og før noen klager – nei, ingen av disse summeringsfunksjonene ser etter eller tar for seg aritmetisk overløp. Det er et emne for en annen dag.
- For tilstrekkelig løse definisjoner av «lett». Metaprogrammeringsspråket som brukes til å støtte maler er Turing-komplett, så du kan gjøre * virkelig fantastisk * og umulig å forstå ting med det.
- For tilstrekkelig løse definisjoner av «hvilken som helst type». Merk at uansett hvilken type du bruker må støtte
+=
operatøren, ellers vil kompilatoren rope på deg. - Denne koden brytes for typer som
unsigned int
ellerlong double
siden de har et mellomrom i navnet. Jeg vet ikke umiddelbart løsningen på det problemet, og jeg har brukt nok tid på dette svaret slik det er.