1)

Nachfolgend finden Sie eine Python-Funktion summation kann die Summe der Würfel / Quadrate / .. ausführen, ähnliche Operationen .

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)

Betrachten Sie unter dem Sortieren der API von C

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); 

Hier kann qsort Daten eines beliebigen Typs , Array von Floats / Dateinamen in einem Verzeichnis / strings / …


Frage:

Wie definiere ich eine generische Funktion?

Ist summation eine generische Funktion?

oder

Ist qsort eine generische Funktion?

oder

Ist die generische Funktion in zwei Beispielen eine ungültige Terminologie?

Hinweis: Motivation-To-Term qsort oder eine von mir entworfene Sortierfunktion

Kommentare

  • Welche Definition von “ generische Funktion “ Haben Sie gelesen, dass Sie ‚ nicht verstehen? Es wäre hilfreich, wenn Sie dies veröffentlichen würden, anstatt eine Menge Code zu schreiben.
  • Der typentheoretische Begriff für die Art von Generik, bei der eine Funktion für jeden Typ ohne Einschränkung und ohne Kenntnis des spezifischen Typs funktioniert, lautet parametrischer Polymorphismus . Die Identitätsfunktion ist auf diese Weise generisch.
  • In einigen Sprachen (wie Java) “ hat die generische Funktion “ eine spezifische technische Definition. Dies ist in Python jedoch nicht der Fall, sodass die generische Funktion “ “ keine genau definierte Bedeutung hat. Dies bedeutet nicht, dass “ eine ungültige Terminologie “ ist, sondern dass Sie den Kontext kennen sollten, wenn Sie den Begriff verwenden.
  • @AndresF. Javascrpt verwendet auch häufig diese generische Funktion Terminologie. Möglicherweise haben Sie eine Funktion, für deren Verarbeitung ein beliebiges HTML-Element erforderlich ist (Beispiel: Löschen Sie alle untergeordneten Elemente des angegebenen HTML-Elements).

Antwort

“ generisch „hat mehrere Bedeutungen.

Informelle Definition

„generic“ in der Alltagssprache etwas, das gemeinsame Eigenschaften hat, aber in gewisser Weise weniger spezifisch ist.

Unter dieser Perspektive können Sie qsort() als generisch betrachten: den Code dieser Funktion kann jede Datenstruktur mit fester Größe sortieren, für die Sie mithilfe des QSORT-Algorithmus eine Vergleichsfunktion definieren können.

Gleiches gilt für Ihre summation() -Funktion, die Begriffe zusammenfasst, die mit beliebigen Funktionen mit einem Parameter erhalten wurden.

Formale Definition

Programmiersprachen wie C ++ oder Java ermöglichen generische Programmierung unter Verwendung von Vorlagen oder Generika:

Definition aus dem C ++ 14-Standard : Eine Vorlage definiert eine Familie von Klassen oder Funktionen oder einen Alias für eine Familie von Typen.

Das Prinzip ist, dass die Implementierung einer Klasse oder Funktion durch Typen parametrisiert werden kann.

Nach dieser formaleren Sichtweise qsort() ist keine generische Funktion. Die Implementierung muss bei der Kompilierung keinen Typ bestimmen, und das Verhalten ist typunabhängig. Das einzige, was es braucht, ist die Größe der zu sortierenden Elemente, und diese Größe ist ein gewöhnliches Argument, das zur Laufzeit verarbeitet wird.

Für eine Sprache, die nicht statisch typisiert ist, wie z. B. Python , bin ich mir nicht sicher, was ich für . Ich denke, es ist nicht generisch, weil seine Implementierung und sein Verhalten nicht typabhängig sind: Diese Funktion ist nur eine Funktion höherer Ordnung, wobei das Argument term lautet eine Funktion. Es wird keine Funktion verwendet, die das Verhalten dieser Funktion basierend auf Typen ändern würde.

Zur Veranschaulichung einer generischen Funktion können Sie sich die C ++ – Standardfunktion ansehen. std::sort() : Die Implementierung hängt vom Typ der Argumente ab (und optional von einer Vergleichsfunktion mit Argumenten eines bestimmten Typs). Mithilfe der Funktionen von C ++ – Vorlagen kann jeder Container eines beliebigen Typs sortiert werden, sofern die Operatoren / Elementfunktionen / Merkmale / Iteratoren vorhanden sind, die für die Implementierung der generischen Funktion erforderlich sind.

Kann eine dynamisch typisierte Sprache generische Funktionen haben?

Dynamisch typisierte Sprache erforderlich weniger generischer Code als statisch typisierte Sprachen.

Wenn Sie beispielsweise einen Container mit Objekten vom dynamischen Typ haben, kann eine qsort-Funktion den Container generisch sortieren, solange eine beliebige Kombination von zwei Elementen im Container verglichen werden kann.

Aber selbst in einer solch flexiblen Umgebung kann eine generische – typabhängige – Programmierung hilfreich sein. Der typische Anwendungsfall sind Multimethoden, bei denen das Verhalten oder der Code vom Typ der Argumente oder sogar von der Kombination der Typen abhängt (z. B. zum Bestimmen des Schnittpunkts zwischen zwei verschiedenen Formen). Weitere Informationen finden Sie unter:

Kommentare

  • Nicht sicher, warum vergleichen wir Generika (hauptsächlich) Wird verwendet, um Typumwandlungen in Java zu vermeiden. & hilft beim Ausführen von Polymorphismus) bei der Definition der generischen Funktion?
  • @overexchange Ich denke, dass Java auch generische Programmierung bietet, einschließlich generischer Methoden (Siehe Spezifikation oder Tutorial ). Trotzdem habe ich ‚ den Definitionsteil leicht bearbeitet, um Ihre Bemerkung zu adressieren.
  • Das generische Paket von Python hat nichts mit generischen Funktionen zu tun. Außer sie haben dasselbe Adjektiv.
  • @Killian, wenn generische Programmierung , handelt es sich um die Idee, von konkreten, effizienten Algorithmen zu abstrahieren Um generische Algorithmen zu erhalten, die mit verschiedenen Datendarstellungen kombiniert werden können , denke ich, dass die Multimethoden in diesem Paket enthalten sein sollten, ‚ glauben Sie das nicht?

Antwort

Generische Funktionen nehmen zum Zeitpunkt der Kompilierung generisch den Typ mindestens eines Funktionsarguments an. Das heißt, der Compiler findet heraus, welcher Typ an einer bestimmten Stelle verwendet wird, und wendet genau diesen Typ an, wo er in der Funktion verwendet wird. Z.B. Wenn Ihre Funktion ein generisches Argument enthält, das mit einem Operator + verwendet wird, muss der Typ über geeignete Methoden verfügen. Für Strings / Arrays wäre dies in vielen Fällen eine Verkettung und für und Integer / Float eine Addition. Der Compiler kann erkennen, dass eine die richtige Operation anwendet. Ihre C-Routine ist in diesem Sinne nicht generisch, da der Programmierer einige Größeninformationen anwendet und nicht der Compiler den Typ erkennt und die richtige Größe verwendet.

ZB in einer fiktiven Sprache

func add(p1,p2) { return p1+p2 } print add("a", "b") // yields "ab" print add(1, 2) // yields 3 

Hier erkennt der Compiler im ersten Fall, dass zwei Zeichenfolgen angewendet werden, und erweitert intern so etwas wie

func add(p1:string, p2:string) 

und behandeln Sie die + als Verkettung, während sie im zweiten Fall

func add(p1:int, p2:int) 

wie angegeben erweitert wird Ganzzahlige Parameter. Generisch bedeutet, dass der Compiler während der Kompilierungszeit individuellen Code generiert. Python ist beispielsweise untypisiert und würde diese Art der Ersetzung zur Laufzeit durchführen. Mittel: Python verfügt nicht über generische Funktionen, da alles generisch ist.

Kommentare

  • Sie haben Ihre Idee nicht verstanden. Sie meinen, + ist eine generische Funktion, Syntax in C ++?
  • Funktionsargumente mit höherer Funktion sind von höherer Ordnung Funktion s in der Python / JavaScript-Welt. In C benötigen wir dafür Funktionszeiger.
  • Siehe meine Bearbeitung oben.
  • Also, welche Art von Funktion ist summation, Funktion höherer Ordnung? und nichts weiter als das?
  • Es gibt viele Definitionen für das, was generisch ist. Stroustrup definiert es beispielsweise als “ -Programmierung unter Verwendung von Typen als Parameter „. Für die Wikipedia-Referenz gehe ich ‚ lieber zu: en.wikipedia.org/wiki/Generic_programming

Antwort

Ich werde dies aus der Perspektive von C ++ beginnen und mich dann in C hineinarbeiten.

In statisch typisierten Sprachen wie C, C ++, Java usw. können Sie mit einer „generischen“ Funktion die Funktionsoperationen einmal angeben und Platzhalter für alle Typen verwenden, die variieren können zwischen verschiedenen Aufrufen (dh Funktionen wie qsort und bsearch sind definitiv nicht generische Funktionen). Idealerweise soll der Compiler auch alle Aufrufe dieser generischen Funktion automatisch erkennen und den tatsächlichen Code nach Bedarf generieren.

C ++ macht dies einfach 1 , indem es Vorlagen anbietet:

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 ist ein Platzhalter für einen beliebigen Typ 2 , sodass Sie ihn als

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 ); 

Wenn der Code kompiliert wird, sieht der Compiler die beiden Aufrufe von summation und leitet die Arten der Argumente ab. Für jeden unterschiedlichen Typ wird eine neue Instanz der Funktion generiert , die einen eindeutigen Namen erhält:

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; ... } 

Anschließend wird Code generiert so dass das Ergebnis von summation_i sumi und summation_d sumd.

C bietet nichts Ähnliches wie die Vorlagenfunktion. Traditionell haben wir die generische Programmierung auf zwei Arten angegriffen – entweder mithilfe von Makros oder mithilfe von void * überall verteilen und typabhängige Operationen an andere Funktionen delegieren.

Hier ist ein schlechtes Beispiel für eine makrobasierte Lösung:

#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; } 

Die SUMMATION_DEF ist ungefähr einer Vorlage insofern ähnlich, als es die Funktionsoperationen angibt, wobei der Makroparameter t als Typplatzhalter verwendet wird. Wir verwenden auch t als Teil des Funktionsnamens – ## ist der Token-Einfügeoperator, und der Präprozessor erweitert t und hängen Sie diesen Wert an den Namen der Funktion 3 an.

Wo er sich von C ++ unterscheidet, ist die Tatsache, dass ein Makro nur eine dumme Textersetzung ist. Es wird nicht ausgelöst spezielle Operationen des Compilers. Die tatsächlichen Funktionsinstanzen werden nicht automatisch basierend auf Aufrufen des Makros SUMMATION generiert – wir müssen die gewünschten Funktionen explizit generieren (daher die SUMMATION_DEF(int) und SUMMATION_DEF(double) vor main). Dies bedeutet auch, wenn wir summation_xxx aufrufen Über das Makro SUMMATION müssen wir den Typ als Teil der Makroargumentliste übergeben, damit die richtige Funktion aufgerufen wird. Was für ein Schmerz.

Die Der C 2011-Standard hat das Schlüsselwort _Generic hinzugefügt, was das Leben in dieser Hinsicht etwas erleichtern kann:

#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 Mit dem Schlüsselwort _Generic können Sie Ausdrücke basierend auf types auswerten. Wenn also der type des ersten Arguments SUMMATION ist int *, wir nennen summation_int; es ist „s double * nennen wir summation_double. Auf diese Weise müssen wir den Typnamen nicht in den Makroargumenten angeben.

Wie Sie gesehen haben, besteht der andere Ansatz darin, void * zu verwenden und typabhängige Operationen an andere Funktionen zu delegieren. Es ist keine wirklich „generische“ Programmierung, da Sie jede Vergleichsfunktion für jeden Typ manuell implementieren müssen. Sie können es nicht nur einmal codieren und damit fertig sein. Mit void * werfen Sie die Typensicherheit grundsätzlich aus dem Fenster in den Gegenverkehr.

Und bevor sich jemand beschwert – nein, keine dieser Summierungsfunktionen prüft oder behandelt arithmetischen Überlauf. Das ist ein Thema für einen anderen Tag.


  1. Für ausreichend lose Definitionen von „einfach“. Die Metaprogrammiersprache, die zur Unterstützung von Vorlagen verwendet wird, ist Turing-complete, sodass Sie * wirklich erstaunliche * und damit unmöglich zu verstehende Dinge tun können.
  2. Für ausreichend lose Definitionen von „jedem Typ“. Beachten Sie, dass jeder Typ, den Sie verwenden, den Operator += unterstützen muss, sonst schreit der Compiler Sie an.
  3. Dieser Code wird für Typen wie unsigned int oder long double unterbrochen, da der Name Leerzeichen enthält. Ich kenne die Lösung für dieses Problem nicht sofort und habe so viel Zeit für diese Antwort aufgewendet.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.