1)
Nedan finns en pythonfunktion summation
, att kan utföra summan av kuber / kvadrater / .., liknande 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)
Tänk nedan på att sortera api från C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Här kan qsort
sortera data av vilken typ som helst , array of floats / file names in a directory / strings / …
Fråga:
Hur man definierar en generisk funktion?
Är summation
en generisk funktion?
eller
Är qsort
en generisk funktion?
eller
Givet två exempel, är Allmän funktion en ogiltig terminologi?
Obs: Motivation-till-term qsort
eller någon sortfunktion som jag utformar
Kommentarer
- Vilken definition av ” generisk funktion ” har du läst att du inte förstår ’? Det skulle hjälpa om du publicerade det istället för att skriva en massa kod.
- Typteoriordet för den typ av genericism där en funktion fungerar för alla typer utan begränsning och utan kunskap om den specifika typen är parametrisk polymorfism . Identitetsfunktionen är generisk på detta sätt.
- På vissa språk (som Java) har ” generisk funktion ” en specifik teknisk definition. Men detta är inte fallet i Python, så ” generisk funktion ” har inte väldefinierad betydelse. Det betyder inte att det är ” ogiltig terminologi ”, bara att du bör vara medveten om sammanhanget när du använder termen.
- @AndresF. Javascrpt använder också denna generiska funktion terminologi mycket. Eftersom du kan ha en funktion som tar alla html-element att bearbeta (exempel – ta bort alla underordnade html-element)
Svar
Det finns flera betydelser av” generisk ”.
Informell definition
”generisk” i vardagsspråket något som delar gemensamma egenskaper men som på vissa sätt är mindre specifika.
Under detta perspektiv kan du överväga qsort()
som generisk: koden för den här funktionen kan sortera valfri datastruktur med fast storlek som du kan definiera en jämförelsefunktion med QSORT-algoritmen.
Detsamma gäller för din summation()
-funktion, som sammanfattar termer som erhållits med hjälp av alla funktioner med en parameter.
Formell definition
Programmeringsspråk som C ++ eller Java möjliggör generisk programmering med hjälp av mallar eller generik:
Definition från C ++ 14-standarden : En mall definierar en familj av klasser eller funktioner eller ett alias för en typ av familj.
Principen är att en klass eller en funktions implementering kan parametriseras efter typer.
Enligt denna mer formella synvinkel, qsort()
är inte en generisk funktion. Dess implementering behöver inte bestämma någon typ vid sammanställning, och dess beteende är typoberoende. Det enda som behövs är storleken på elementen som sorteras, och den här storleken är ett vanligt argument som bearbetas vid körning.
För ett språk som inte är typiskt skrivet som Python är jag inte säker på vad jag ska svara för summation()
. Jag tror att det inte är generiskt eftersom dess implementering och dess beteende inte är typberoende: den här funktionen är bara en funktion av högre ordning, med argumentet term
en funktion. Den använder inte någon funktion som kan ändra funktionen för denna funktion baserat på typer.
För att illustrera en generisk funktion kan du titta på C ++ standardfunktionen std::sort()
: dess implementering beror på typen av argument (och eventuellt en jämförelsefunktion med argument av en bestämd typ). Genom att använda funktionerna i C ++ – mallar kan den sortera valfri behållare av vilken typ som helst, under förutsättning att den har de operatörer / medlemsfunktioner / egenskaper / iteratorer som krävs för implementeringen av den generiska funktionen.
Kan ett dynamiskt skrivat språk ha generiska funktioner
Dynamiskt skrivat språk kräver mindre generisk kod än statligt skrivna språk.
Om du till exempel har en behållare med objekt av dynamisk typ kan en qsort-funktion generellt sortera behållaren så länge som en kombination av två element i behållaren kan jämföras.
Men även i en sådan flexibel miljö kan generisk – typberoende – programmering vara till hjälp. Det typiska användningsfallet är multimetoder, där beteendet eller koden beror på typen av argument eller till och med kombinationen av typer (t.ex. för att bestämma skärningspunkten mellan två olika former). För ytterligare information se:
- Generiskt programmeringspaket för python: multidispatching
- Gör generiska funktioner användbara i smalltalk
- Exempel på generisk funktion i Common Lisp
Kommentarer
- Inte säker, varför jämför vi Generics (främst används för att undvika typgjutning i Java & hjälper till att utföra polymorfism) med definition av generisk funktion?
- @overexchange Jag tror att java också erbjuder generisk programmering, inklusive generiska metoder (se specifikation eller självstudie ). Ändå har jag ’ redigerat definitionsdelen något för att ta itu med din kommentar.
- Generic-paketet från Python har inget att göra med generiska funktioner. Förutom att de delar samma adjektiv.
- @Killian om generisk programmering , handlar om tanken på att abstrahera från konkreta, effektiva algoritmer för att erhålla generiska algoritmer som kan kombineras med olika datarepresentationer , tror jag att multimetoderna i det paketet ska vara i, inte ’ tänker du det?
Svar
Generiska funktioner tar generellt typ av minst ett funktionsargument vid sammanställningstid. Det vill säga, kompilatorn får reda på vilken typ som används på en viss plats och tillämpar exakt denna typ där den används i funktionen. T.ex. om du har ett generiskt argument i din funktion som används med en operatör +
, måste typen ha lämpliga metoder. För strängar / matriser skulle detta i många fall vara en sammankoppling och för och heltal / flyta ett tillägg. Kompilatorn kan upptäcka att en tillämpa rätt operation. Din C-rutin är inte generisk i den meningen, eftersom det är programmeraren som tillämpar viss storleksinformation och inte kompilatorn upptäcker typen och använder rätt storlek.
Till exempel på något fiktivt språk
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Här upptäcker kompilatorn i det första fallet att två strängar tillämpas och utvidgas internt något liknande
func add(p1:string, p2:string)
och behandla +
som sammankoppling medan det i det andra fallet skulle expandera
func add(p1:int, p2:int)
enligt leveransen heltalsparametrar. Generiska medel, kompilatorn genererar enskild kod under kompileringstiden. Python är till exempel otypat och skulle göra den typen av ersättning under körning. Medel: Python har inte generiska funktioner eftersom allt är typiskt generiskt.
Kommentarer
- Fick inte din idé. du menar + är en generisk funktion, syntax i C ++?
- Argument för funktionshämtande funktion är högre ordning fungera s i Python / JavaScript-världen. I C behöver vi funktionspekare för samma.
- Se min redigering ovan.
- Så vilken typ av funktion är
summation
, Högre orderfunktion? och inget mer än det? - Det finns många definitioner för vad generiskt är. Stroustrup definierar det till exempel som ” programmering med typer som parametrar ”. För wikipedia-referensen går jag ’ hellre till: en.wikipedia.org/wiki/Generic_programming
Svar
Jag ska börja detta från C ++ perspektiv och sedan arbeta mig in i C.
På statiskt skrivna språk som C, C ++, Java, etc. tillåter en ”generisk” funktion att ange funktionsåtgärderna en gång med platshållare för alla typer som kan variera mellan olika samtal (vilket innebär att funktioner som qsort
och bsearch
är definitivt inte generiska funktioner). Helst skulle du också vilja att kompilatorn automatiskt upptäcker samtal till den här generiska funktionen och genererar den faktiska koden vid behov.
C ++ gör det enkelt 1 genom att erbjuda mallar :
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
är en platshållare för alla typer 2 , så du kan kalla 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 kompileras ser kompilatorn de två anropen till summation
och ärrör typerna av argumenten. För varje annan typ enererar den en ny instans av funktionen, vilket ger den ett unikt namn:
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 genererar sedan kod så att resultatet av summation_i
tilldelas sumi
och summation_d
tilldelas sumd
.
C erbjuder inte något som liknar mallfunktionen. Traditionellt har vi attackerat generisk programmering på ett av två sätt – antingen genom att använda makron eller genom att använda void *
överallt och delegera typmedvetna operationer till andra funktioner.
Här är ett dåligt exempel på en makrobaserad 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
liknar ungefär en mall genom att den anger funktionsfunktionerna, med hjälp av makroparametern t
som typplatshållare. Vi använder också t
som en del av funktionsnamnet – ##
är token-klistrande operatör och förprocessorn kommer att expandera t
och lägg till det värdet till namnet på funktionen 3 .
Där det skiljer sig från C ++ är det faktum att ett makro bara är en dum textersättning. Det utlöser inte eventuella specialoperationer från kompilatorn. De faktiska funktionsinstanserna genereras inte automatiskt baserat på alla anrop av SUMMATION
makroen – vi måste uttryckligen generera de funktioner vi vill ha (därav SUMMATION_DEF(int)
och SUMMATION_DEF(double)
före main
). Det betyder också att när vi kallar summation_xxx
genom SUMMATION
makro måste vi skicka typen som en del av makroargumentlistan, så att rätt funktion blir kallad. Vilken smärta.
C 2011-standarden lade till sökordet _Generic
, vilket kan göra livet lite enklare i det avseendet:
#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
nyckelord gör att du kan utvärdera uttryck baserat på typer ; alltså, om typen för det första argumentet till SUMMATION
är int *
, vi kallar summation_int
; det är ”s it” s double *
, vi kallar summation_double
. På detta sätt behöver vi inte ange typnamnet i makroargumenten.
Den andra metoden, som du har sett, är att använda void *
och att delegera typmedvetna operationer till andra funktioner. Som jag sa ovan, att ” s egentligen inte ”generisk” programmering, eftersom du måste implementera varje jämförelsefunktion manuellt för varje typ. Du kan inte bara koda det en gång och vara klar med det. Och genom att använda void *
kastar du i princip typsäkerhet ut genom fönstret och i mötande trafik.
Och innan någon klagar – nej, ingen av dessa summeringsfunktioner söker efter eller hanterar aritmetisk överflöd. Det är ett ämne för en annan dag.
- För tillräckligt lösa definitioner av ”lätt”. Metaprogrammeringsspråket som används för att stödja mallar är Turing-komplett, så du kan göra * riktigt fantastiskt * och omöjligt att förstå saker med det.
- För tillräckligt lösa definitioner av ”vilken typ som helst”. Observera att vilken typ du använder måste stödja operatorn
+=
, annars skriker kompilatorn åt dig. - Den här koden bryts för typer som
unsigned int
ellerlong double
eftersom de har ett blanksteg i namnet. Jag vet inte omedelbart lösningen på det problemet och jag har spenderat tillräckligt med tid på det här svaret som det är.