1)
Di seguito è riportata una funzione python summation
, che può eseguire la somma di cubi / quadrati / .., operazioni simili .
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)
Considera, sotto lordinamento delle API da C,
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Qui, qsort
può ordinare dati di qualsiasi tipo , array di float / nomi di file in una directory / strings / …
Domanda:
Come definire una funzione generica?
summation
è una funzione generica?
o
qsort
è una funzione generica?
o
Dati due esempi, Funzione generica è una terminologia non valida?
Nota: termine Motivation-To qsort
o qualsiasi funzione di ordinamento da me progettata
Commenti
- Quale definizione di ” funzione generica ” hai letto che ‘ non capisci? Sarebbe utile se lo pubblicassi invece di scrivere un mucchio di codice.
- Il termine della teoria dei tipi per il tipo di genericità in cui una funzione funziona per qualsiasi tipo senza vincoli e senza conoscenza del tipo specifico è polimorfismo parametrico . La funzione di identità è generica in questo modo.
- In alcune lingue (come Java) la ” funzione generica ” ha una definizione tecnica specifica. Ma questo non è il caso di Python, quindi la ” funzione generica ” non ha un significato ben definito. Non significa che sia ” terminologia non valida “, ma solo che devi essere consapevole del contesto quando usi il termine.
- @AndresF. Javascrpt utilizza molto anche questa terminologia della funzione generica . Perché potresti avere una funzione che richiede qualsiasi elemento html da elaborare (esempio: elimina tutti i figli di un dato elemento html)
Risposta
Ci sono diversi significati per” generico “.
Definizione informale
“generico” nel linguaggio quotidiano qualcosa che condivide proprietà comuni ma in qualche modo è meno specifico.
In questa prospettiva, potresti considerare qsort()
come generico: il codice di questa funzione è in grado di ordinare qualsiasi struttura dati a dimensione fissa per la quale è possibile definire una funzione di confronto utilizzando lalgoritmo QSORT.
Lo stesso vale per la funzione summation()
, che riassume i termini ottenuti utilizzando qualsiasi funzione con un parametro.
Definizione formale
Linguaggi di programmazione come C ++ o Java consentono la programmazione generica con luso di modelli o generici:
Definizione dallo standard C ++ 14 : un modello definisce una famiglia di classi o funzioni o un alias per una famiglia di tipi.
Il principio è che limplementazione di una classe o di una funzione può essere parametrizzata per tipi.
Secondo questo punto di vista più formale, qsort()
non è una funzione generica. La sua implementazione non ha bisogno di determinare alcun tipo durante la compilazione e il suo comportamento è indipendente dal tipo. Lunica cosa di cui ha bisogno è la dimensione degli elementi da ordinare, e questa dimensione è un argomento ordinario che viene elaborato in fase di esecuzione.
Per un linguaggio che non è tipizzato staticamente come Python , “non so cosa rispondere per summation()
. Penso che non sia generico perché la sua implementazione e il suo comportamento non dipendono dal tipo: questa funzione è solo una funzione di ordine superiore, con largomento term
una funzione. Non utilizza alcuna caratteristica che possa alterare il comportamento di questa funzione in base ai tipi.
Per lillustrazione di una funzione generica, potresti dare unocchiata alla funzione standard C ++ std::sort()
: la sua implementazione dipende dal tipo dei suoi argomenti (e opzionalmente una funzione di confronto con argomenti di un determinato tipo). Utilizzando le funzionalità dei modelli C ++, può ordinare qualsiasi contenitore di qualsiasi tipo, a condizione che abbia gli operatori / funzioni membro / tratti / iteratori richiesti dallimplementazione della funzione generica.
Un linguaggio tipizzato dinamico può avere funzioni generiche
Il linguaggio tipizzato dinamicamente richiede codice meno generico rispetto ai linguaggi tipizzati staticamente.
Ad esempio, se si dispone di un contenitore di oggetti di tipo dinamico, una funzione qsort potrebbe ordinare genericamente il contenitore, purché sia possibile confrontare qualsiasi combinazione di due elementi nel contenitore.
Ma anche in un ambiente così flessibile, la programmazione generica – dipendente dal tipo– potrebbe essere utile. Il caso duso tipico è multimetodi, in cui il comportamento o il codice dipende dal tipo di argomenti o anche dalla combinazione di tipi (come per determinare lintersezione tra due forme diverse). Per ulteriori informazioni, vedere:
- Pacchetto di programmazione generico per python: multidispatching
- Rendere utilizzabili funzioni generiche in smalltalk
- Esempio di funzione generica in Common Lisp
Commenti
- Non sono sicuro, perché stiamo confrontando Generici (principalmente utilizzato per evitare il casting del tipo in Java & aiuta a eseguire il polimorfismo) con la definizione di funzione generica?
- @overexchange Penso che java offra anche programmazione generica, inclusi metodi generici (vedi la specifica o il tutorial ). Tuttavia ho ‘ modificato leggermente la parte della definizione per rispondere alla tua osservazione.
- Il pacchetto Generic di Python non ha nulla a che fare con le funzioni generiche. Ad eccezione del fatto che condividono lo stesso aggettivo.
- @Killian if programmazione generica , riguarda l idea di astrarre da algoritmi concreti ed efficienti per ottenere algoritmi generici che possono essere combinati con diverse rappresentazioni di dati , penso che i multimetodi in quel pacchetto dovrebbero essere in, don ‘ credi di sì?
Risposta
Le funzioni generiche prendono genericamente il tipo di almeno un argomento di funzione in fase di compilazione. Cioè, il compilatore scopre quale tipo viene utilizzato in un determinato luogo e applica esattamente questo tipo dove viene utilizzato nella funzione. Per esempio. se hai un argomento generico nella tua funzione che viene utilizzato con un operatore +
, il tipo deve avere metodi appropriati. Per stringhe / array questo sarebbe in molti casi una concatenazione e for e integer / float unaddizione. Il compilatore può rilevare che applica loperazione corretta. La tua routine C non è generica in questo senso, poiché è il programmatore che applica alcune informazioni sulla dimensione e non il compilatore che rileva il tipo e utilizza la dimensione corretta.
Ad esempio in un linguaggio fittizio
func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3
Qui il compilatore rileva nel primo caso che vengono applicate due stringhe e si espanderà internamente qualcosa di simile
func add(p1:string, p2:string)
e tratta +
come una concatenazione mentre nel secondo caso si espanderebbe
func add(p1:int, p2:int)
come da fornito parametri interi. Generico significa che il compilatore genera codice individuale durante la compilazione. Python, ad esempio, non è tipizzato e farebbe quel tipo di sostituzione durante il runtime. Significa: Python non ha funzioni generiche poiché tutto è in qualche modo generico.
Commenti
- Non hai avuto la tua idea. vuoi dire che + è una funzione generica, la sintassi in C ++?
- La funzione che accetta gli argomenti della funzione è di ordine superiore funzione s nel mondo Python / JavaScript. In C, abbiamo bisogno di puntatori a funzione, per lo stesso.
- Vedi la mia modifica sopra.
- Quindi, che tipo di funzione è
summation
, Funzione di ordine superiore? e nientaltro? - Ci sono molte definizioni per ciò che è generico. Stroustrup, ad esempio, lo definisce come ” programmazione utilizzando i tipi come parametri “. Per il riferimento a wikipedia, ‘ preferisco andare a: en.wikipedia.org/wiki/Generic_programming
Risposta
Inizierò dal punto di vista di C ++, quindi procederò a modo mio in C.
In linguaggi di tipo statico come C, C ++, Java, ecc., una funzione “generica” consente di specificare le operazioni della funzione una volta , utilizzando segnaposto per qualsiasi tipo che può variare tra chiamate diverse (il che significa che funzioni come qsort
e bsearch
sono decisamente non funzioni generiche). Idealmente, ti piacerebbe anche che il compilatore rilevi automaticamente qualsiasi chiamata a questa funzione generica e generi il codice effettivo se necessario.
C ++ semplifica questo 1 offrendo modelli :
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
è un segnaposto per qualsiasi tipo 2 , quindi puoi chiamarlo come
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 );
Quando il codice viene compilato, il compilatore vede le due chiamate a summation
e deduce i tipi degli argomenti. Per ogni diverso tipo, genera una nuova istanza della funzione, assegnandole un nome univoco:
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; ... }
Quindi genera il codice in modo tale che il risultato di summation_i
sia assegnato a sumi
e summation_d
sia assegnato a sumd
.
C non offre nulla di simile alla funzionalità dei modelli. Tradizionalmente, abbiamo attaccato la programmazione generica in uno dei due modi seguenti: utilizzando le macro o utilizzando void *
ovunque e delegando le operazioni in grado di riconoscere i tipi ad altre funzioni.
Ecco “un cattivo esempio di una soluzione basata su 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; }
Il SUMMATION_DEF
è più o meno simile a un modello in quanto specifica le operazioni della funzione, utilizzando il parametro macro t
come segnaposto di tipo. Usiamo anche t
come parte del nome della funzione: ##
è loperatore per incollare token e il preprocessore espanderà t
e aggiungi quel valore al nome della funzione 3 .
Dove differisce da C ++ è il fatto che una macro è solo una muta sostituzione di testo. Non si attiva eventuali operazioni speciali da parte del compilatore. Le istanze delle funzioni effettive non vengono generate automaticamente in base a qualsiasi invocazione della macro SUMMATION
: dobbiamo generare esplicitamente le funzioni che vogliamo (da qui il SUMMATION_DEF(int)
e SUMMATION_DEF(double)
prima di main
). Significa anche che quando chiamiamo summation_xxx
attraverso la macro SUMMATION
, dobbiamo passare il tipo come parte dellelenco degli argomenti della macro, in modo che venga chiamata la funzione giusta. Che dolore.
Il Lo standard C 2011 ha aggiunto la parola chiave _Generic
, che può semplificarti un po la vita sotto questo aspetto:
#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; }
Il La parola chiave _Generic
consente di valutare espressioni in base a tipi ; quindi, se il tipo del primo argomento SUMMATION
è int *
, chiamiamo summation_int
; it “s it” s double *
, chiamiamo summation_double
. In questo modo non dobbiamo specificare il nome del tipo negli argomenti della macro.
Laltro approccio, come hai visto, consiste nellusare void *
e delegare le operazioni di riconoscimento dei tipi ad altre funzioni. Come ho detto sopra, ” Non è una programmazione “generica”, poiché devi implementare manualmente ogni funzione di confronto per ogni tipo. Non puoi codificarlo una sola volta e farla finita. E utilizzando void *
, fondamentalmente lanci la protezione dai tipi fuori dalla finestra e nel traffico in arrivo.
E prima che qualcuno si lamenti – no, nessuna di queste funzioni di sommatoria controlla o tratta loverflow aritmetico. Questo è un argomento per un altro giorno.
- Per definizioni sufficientemente sciolte di “facile”. Il linguaggio di metaprogrammazione utilizzato per supportare i modelli è completo di Turing, quindi puoi fare * davvero sorprendente * e impossibile capire le cose con esso.
- Per definizioni sufficientemente sciolte di “qualsiasi tipo”. Nota che qualunque tipo tu usi deve supportare loperatore
+=
, altrimenti il compilatore ti sgriderà. - Questo codice verrà interrotto per tipi come
unsigned int
olong double
poiché hanno spazi nel nome. Non so immediatamente la soluzione a quel problema e ho dedicato abbastanza tempo a questa risposta così comè.