1)
Mai jos este o funcție python summation
poate efectua suma de cuburi / pătrate / .., operațiuni similare .
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)
Luați în considerare, mai jos sortarea api din C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Aici, qsort
poate sorta date de orice tip , matrice de flotante / nume de fișiere într-un director / șiruri / …
Întrebare:
Cum se definește o funcție generică?
Este summation
o funcție generică?
sau
Este qsort
o funcție generică?
sau
Având în vedere două exemple, este Funcția generică o terminologie nevalidă?
Notă: Termen de motivare-To qsort
sau orice funcție de sortare pe care o proiectez eu
Comentarii
- Ce definiție a ” funcție generică ” ați citit că nu ‘ nu înțelegeți? Ar fi util dacă ați publica acest lucru în loc să scrieți o grămadă de cod.
- Termenul teoriei tipului pentru tipul de genericism în care o funcție funcționează pentru orice tip fără constrângere și fără cunoașterea tipului specific este polimorfism parametric . Funcția de identitate este generică în acest mod.
- În unele limbi (cum ar fi Java) ” funcția generică ” are o definiție tehnică specifică. Dar acest lucru nu este cazul în Python, deci ” funcția generică ” nu are o semnificație bine definită. Nu înseamnă că este ” terminologie nevalidă „, doar că ar trebui să fiți conștient de context atunci când utilizați termenul.
- @AndresF. Javascrpt folosește foarte mult această terminologie funcție generică . Deoarece este posibil să aveți o funcție care să prelucreze orice element html (exemplu – ștergeți toți copiii unui anumit element html)
Răspuns
Există mai multe semnificații pentru” generic „.
Definiție informală
„generic” în limbajul de zi cu zi ceva care împărtășește proprietăți comune, dar este mai puțin specific în anumite privințe.
În această perspectivă, ați putea considera qsort()
ca generic: codul acestei funcții este capabil să sorteze orice structură de date de dimensiuni fixe pentru care puteți defini o funcție de comparație utilizând algoritmul QSORT.
Același lucru se aplică funcției dvs. summation()
, care rezumă termenii obținuți utilizând orice funcții cu un singur parametru.
Definiție formală
Limbajele de programare precum C ++ sau Java permit programarea generică cu utilizarea de șabloane sau generice:
Definiție din standardul C ++ 14 : Un șablon definește o familie de clase sau funcții sau un alias pentru o familie de tipuri.
Principiul este că implementarea unei clase sau a unei funcții poate fi parametrizată pe tipuri.
Conform acestui punct de vedere mai formal, qsort()
nu este o funcție generică. Implementarea sa nu trebuie să determine orice tip la compilare, iar comportamentul său este independent de tip. Singurul lucru de care are nevoie este dimensiunea elementelor sortate, iar această dimensiune este un argument obișnuit care este procesat în timpul rulării.
Pentru o limbă care nu este tastată static, cum ar fi Python , nu sunt sigur ce să răspund pentru summation()
. Cred că nu este generic deoarece implementarea și comportamentul său nu depind de tip: această funcție este doar o funcție de ordin superior, argumentul term
fiind o funcție. Nu folosește nicio caracteristică care ar modifica comportamentul acestei funcții pe baza tipurilor.
Pentru ilustrarea unei funcții generice, puteți arunca o privire la funcția standard C ++ std::sort()
: implementarea sa depinde de tipul argumentelor sale (și opțional o funcție de comparație cu argumente de tip determinat). Folosind caracteristicile șabloanelor C ++, poate sorta orice container de orice tip, cu condiția să aibă operatorii / funcțiile membre / trăsăturile / iteratorii care sunt solicitați de implementarea funcției generice.
Poate un limbaj tastat dinamic să aibă funcții generice
Limbajul tastat dinamic necesită cod mai puțin generic decât limbile tastate static.
De exemplu, dacă aveți un container de obiecte de tip dinamic, o funcție qsort ar putea sorta generic containerul, atâta timp cât orice combinație de două elemente din container poate fi comparată.
Dar chiar și într-un mediu atât de flexibil, programarea generică – dependentă de tip – ar putea fi utilă. Cazul tipic de utilizare este multimetodele, în care comportamentul sau codul depind de tipul argumentelor sau chiar de combinația de tipuri (cum ar fi determinarea intersecției dintre două forme diferite). Pentru informații suplimentare, consultați:
- Pachet de programare generic pentru python: multidispatching
- Efectuarea funcțiilor generice utilizabile în smalltalk
- Exemplu de funcție generică în Common Lisp
Comentarii
- Nu sunt sigur, de ce comparăm Generice (în principal folosit pentru a evita turnarea tipurilor în Java & ajută la realizarea polimorfismului) cu definirea funcției generice?
- @overexchange Cred că java oferă și programare generică, inclusiv metode generice (consultați specificație sau tutorial ). Cu toate acestea, am ‘ editat ușor partea de definiție pentru a răspunde observației dvs.
- Pachetul generic de la Python nu are nicio legătură cu funcțiile generice. Cu excepția faptului că împărtășesc același adjectiv.
- @Killian dacă programare generică , este despre ideea de a se abstra din algoritmi concreți și eficienți pentru a obține algoritmi generici care pot fi combinați cu diferite reprezentări de date , cred că multimetodele din acel pachet ar trebui să fie, nu ‘ crezi că da?
Răspuns
Funcțiile generice iau tipul a cel puțin un argument de funcție generic la momentul compilării. Adică, compilatorul află ce tip este utilizat într-un anumit loc și aplică exact acest tip acolo unde este utilizat în funcție. De exemplu. dacă aveți un argument generic în funcția dvs. care este utilizat cu un operator +
, tipul trebuie să aibă metode adecvate. Pentru șiruri / tablouri, aceasta ar fi în multe cazuri o concatenare, iar pentru și întreg / plutitor o adăugare. Compilatorul poate detecta că aplică operația corectă. Rutina dvs. C nu este generică în acest sens, deoarece programatorul aplică anumite informații despre dimensiune și nu compilatorul detectează tipul și folosește dimensiunea corectă.
De exemplu, într-un limbaj fictiv
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Aici compilatorul detectează în primul caz că sunt aplicate două șiruri și va extinde intern ceva de genul
func add(p1:string, p2:string)
și tratează +
ca concatenare, în timp ce în al doilea caz se va extinde
func add(p1:int, p2:int)
conform furnizării parametri întregi. Generic înseamnă că compilatorul generează cod individual în timpul compilării. Python, de exemplu, este netipat și ar face acest tip de substituție în timpul rulării. Înseamnă: Python nu are funcții generice, deoarece totul este un fel de generic.
h3> Comentarii
- Nu ți-ai dat seama. vrei să spui că + este o funcție generică, sintaxa în C ++?
- Argumentele funcției care acceptă funcții sunt de ordin superior funcţie s în lumea Python / JavaScript. În C, avem nevoie de indicatori de funcții, pentru aceiași.
- Consultați modificarea mea de mai sus.
- Deci, ce fel de funcție este
summation
, Funcție de comandă superioară? și nimic mai mult decât atât? - Există o mulțime de definiții despre ceea ce este genericul. Stroustrup, de exemplu, îl definește ca ” programare utilizând tipuri ca parametri „. Pentru referința pe Wikipedia, ‘ merg mai degrabă la: en.wikipedia.org/wiki/Generic_programming
Răspuns
Voi începe acest lucru din perspectiva C ++, apoi mă voi îndrepta spre C.
În limbaje tipizate static, cum ar fi C, C ++, Java etc., o funcție „generică” vă permite să specificați operațiunile funcției o dată , utilizând substituenți pentru orice tipuri care pot varia între apeluri diferite (ceea ce înseamnă funcții precum qsort
și bsearch
sunt cel mai sigur not funcții generice). În mod ideal, v-ar plăcea, de asemenea, compilatorul să detecteze automat orice apeluri către această funcție generică și să genereze codul real, după cum este necesar.
C ++ face acest lucru ușor 1 oferind șabloane :
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
este un substituent pentru orice tip 2 , deci îl puteți numi ca
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 );
Când codul este compilat, compilatorul vede cele două apeluri către summation
și deduce tipurile de argumente. Pentru fiecare tip diferit, generează o nouă instanță a funcției, oferindu-i un nume unic:
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; ... }
Apoi generează cod astfel încât rezultatul summation_i
este atribuit sumi
și summation_d
este atribuit sumd
.
C nu oferă nimic similar cu caracteristica șablon. În mod tradițional, am atacat programarea generică într-unul din cele două moduri – fie utilizând macrocomenzi, fie utilizând void *
peste tot și delegarea operațiunilor conștiente de tip către alte funcții.
Aici „un exemplu prost de soluție bazată pe macro:
#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
este aproximativ similar cu un șablon prin faptul că specifică operațiile funcției, utilizând parametrul macro t
ca substituent de tip. De asemenea, folosim t
ca parte a numelui funcției – ##
este operatorul de lipire a simbolurilor, iar preprocesorul se va extinde t
și adăugați această valoare la numele funcției 3 .
Unde diferă de C ++ este faptul că o macrocomandă este doar o substituție de text prost. Nu declanșează orice operații speciale din partea compilatorului. Instanțele de funcții reale nu sunt generate automat pe baza invocațiilor macro-ului SUMMATION
– trebuie să generăm în mod explicit funcțiile dorite (de unde SUMMATION_DEF(int)
și SUMMATION_DEF(double)
înainte de main
). De asemenea, înseamnă că atunci când apelăm summation_xxx
prin macrocomanda SUMMATION
, trebuie să trecem tipul ca parte a listei de argumente macro, astfel încât funcția corectă să fie numită. Ce durere.
Standardul C 2011 a adăugat cuvântul cheie _Generic
, care poate ușura viața în acest sens:
#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; }
Cuvântul cheie _Generic
vă permite să evaluați expresii bazate pe tipuri ; astfel, dacă tipul primului argument către SUMMATION
este int *
, numim summation_int
; este „s it” s double *
, numim summation_double
. În acest fel, nu trebuie să specificăm numele tipului în argumentele macro.
Cealaltă abordare, așa cum ați văzut, este de a utiliza void *
și de a delega operațiuni conștiente de tip către alte funcții. Așa cum am spus mai sus, că ” Programarea nu este „generică”, deoarece trebuie să implementați manual fiecare funcție de comparație pentru fiecare tip. Nu îl puteți codifica o singură dată și puteți termina cu acesta. Și utilizând void *
, aruncați practic siguranța tipului pe fereastră și în traficul care se apropie.
Și înainte ca cineva să se plângă – nu, niciuna dintre aceste funcții de însumare nu verifică sau nu se ocupă de depășirea aritmetică. Acesta este un subiect pentru o altă zi.
- Pentru definiții suficient de libere ale „ușor”. Limbajul de metaprogramare folosit pentru a sprijini șabloanele este complet Turing, deci puteți face * cu adevărat uimitor * și imposibil de înțeles lucrurile cu el.
- Pentru definiții suficient de libere ale „oricărui tip”. Rețineți că orice tip pe care îl utilizați trebuie să accepte operatorul
+=
, altfel compilatorul vă va țipa. - Acest cod se va sparge pentru tipuri precum
unsigned int
saulong double
, deoarece acestea au spațiu alb în nume. Nu știu imediat soluția la această problemă și am petrecut suficient timp cu acest răspuns așa cum este.