2010-03-29 5 views
16

Gibt es eine „richtige“ Art und Weise Funktionen höherer Ordnung in C zu implementierenFunktionen höherer Ordnung in C

Ich bin meistens neugierig über Dinge wie Portabilität und Syntax Korrektheit hier und wenn es mehr als eine Art und Weise, was die Verdienste und Fehler sind. Der Grund, warum ich wissen möchte, wie man Funktionen höherer Ordnung erstellt, ist, dass ich ein System geschrieben habe, um PyObject-Listen (die man beim Aufruf von Python-Skripten erhält) in eine Liste von C-Strukturen zu schreiben, die dieselben Daten enthalten in einer Weise organisiert, die nicht von den python.h-Bibliotheken abhängig ist. Mein Plan ist also, eine Funktion zu haben, die durch eine Pythonie-Liste iteriert und eine Funktion für jedes Element in der Liste aufruft und das Ergebnis in eine Liste stellt, die dann zurückgegeben wird.

Das ist also im Grunde mein Plan:

typedef gpointer (converter_func_type)(PyObject *) 

gpointer converter_function(PyObject *obj) 
{ 
    // do som stuff and return a struct cast into a gpointer (which is a void *) 
} 

GList *pylist_to_clist(PyObject *obj, converter_func_type f) 
{ 
    GList *some_glist; 
    for each item in obj 
    { 
     some_glist = g_list_append(some_glist, f(item)); 
    } 
    return some_glist; 
} 

void some_function_that_executes_a_python_script(void) 
{ 
    PyObject *result = python stuff that returns a list; 
    GList *clist = pylist_to_clist(result, converter_function); 
} 

Und die Frage clearify: Ich möchte wissen, wie das richtig C. in sicherer und mehr tun, würde ich wirklich die Funktion höherer Ordnung Stil halten möchte Aber wenn das verpönt ist, schätze ich sehr, wie ich das anders machen kann.

+1

Gute Frage, aber vage. Es sieht so aus, als hättest du etwas darüber geforscht. Könntest du deine Frage aktualisieren, um anzuzeigen, was du bereits gefunden hast? Die gegebenen Antworten sind großartig, aber können nur so spezifisch sein wie die Frage. –

Antwort

4

Wenn Sie daran interessiert sind, dies in Plain C zu tun, müssen Sie daran denken, einen Kontextzeiger vom Aufrufer des Funktors (die Funktion höherer Ordnung) an die übergebene Funktion zu übergeben lässt Sie genug von einem Abschluss simulieren, damit die Dinge leicht genug funktionieren. Was dieser Zeiger auf ... Nun, das liegt an dir, aber es sollte ein void* in der Funktor-API sein (oder einer der vielen Aliase dafür, wie gpointer in der GLib-Welt oder ClientData in der Tcl C-API) .

[EDIT]: Zur Nutzung/Anpassung Ihr Beispiel:

typedef gpointer (converter_func_type)(gpointer,PyObject *) 

gpointer converter_function(gpointer context_ptr,PyObject *obj) 
{ 
    int *number_of_calls_ptr = context_ptr; 
    *number_of_calls_ptr++; 
    // do som stuff and return a struct cast into a gpointer (which is a void *) 
} 

GList *pylist_to_clist(PyObject *obj, converter_func_type f, gpointer context_ptr) 
{ 
    GList *some_glist; 
    for each item in obj 
    { 
     some_glist = g_list_append(some_glist, f(context_ptr,item)); 
    } 
    return some_glist; 
} 

void some_function_that_executes_a_python_script(void) 
{ 
    int number_of_calls = 0; 
    PyObject *result = python stuff that returns a list; 
    GList *clist = pylist_to_clist(result, converter_function, &number_of_calls); 
    // Now number_of_calls has how often converter_function was called... 
} 

Dies ist ein triviales Beispiel dafür, wie es zu tun, aber es sollte Ihnen den Weg zeigen.

+0

Danke :) Das ist eine wirklich gute Antwort. – Hobblin

0

Wenn Sie Funktionen höherer Ordnung erstellen möchten, verwenden Sie nicht C. Es gibt C-Lösungen für Ihr Problem. Sie sind vielleicht nicht elegant, oder sie sind eleganter, als Sie erkennen.

[Bearbeiten] Ich schlug vor, dass der einzige Weg, dies zu erreichen, war eine Skriptsprache zu verwenden. Andere haben mich darauf hingewiesen. Also, ich ersetze diesen Vorschlag mit diesem: [/ Edit]

Was versuchen Sie zu erreichen? Wenn Sie Schließungen imitieren möchten, verwenden Sie eine Sprache, die sie unterstützt (Sie können Ruby, Lua, Javascript usw. über Bibliotheken anbinden). Wenn Sie Rückrufe verwenden möchten, sind Funktionszeiger in Ordnung. Funktionszeiger kombinieren die gefährlichsten Bereiche von C (Zeiger und das schwache System), seien Sie also vorsichtig. Function Pointer Deklarationen sind auch nicht lustig zu lesen.

Sie finden einige C-Bibliotheken mit Funktionszeigern, weil sie müssen. Wenn Sie eine Bibliothek schreiben, müssen Sie sie vielleicht auch verwenden. Wenn Sie sie nur in Ihrem eigenen Code verwenden, denken Sie wahrscheinlich nicht an C. Sie denken in Lisp oder Schema oder Ruby oder ... und versuchen, es in C zu schreiben. Lernen Sie den C-Weg.

+0

Funktionen höherer Ordnung sind "nur" Funktionen, die entweder eine oder mehrere Funktionen als Argumente annehmen oder eine Funktion zurückgeben. All dies kann in C mit Zeigern zur Funktion erfolgen. Das "bloß" wird zitiert, weil, während die Mechanik davon relativ einfach sein kann (wenn auch mehr oder weniger natürlich und extensiv von verschiedenen Sprachen unterstützt wird), die Verwendung von Funktionen höherer Ordnung zu sehr leistungsfähiger und mind-bending Programmierung führen kann. – mjv

+1

Höhere Reihenfolge! = Dynamischer Code. Eine Funktion höherer Ordnung ist lediglich eine Funktion, die auf Funktionen arbeitet, was in C perfekt machbar ist. Ein Sortieralgorithmus mit einer steckbaren Vergleichsfunktion ist eine Funktion höherer Ordnung. Ein numerischer Integrator ist. Eine Timer-Rückruf-Registrierung ist. Die größte Herausforderung besteht darin, dass Sie die Syntax lernen müssen, die sich von der Sprache unterscheidet. –

+0

Was Daniel und mjv gesagt haben ... – Hobblin

4

In geraden c, das ist wirklich nur durch Funktionszeiger, die beide ein Schmerz und nicht für diese Art von Sache (was teilweise, warum sie ein Schmerz sind) sind getan. Blöcke (oder Verschlüsse, nach Nicht-Apfel) sind dafür fantastisch. Sie kompilieren in gcc-4.x oder etwas und icc etwas, aber egal, was Sie suchen. Leider kann ich keine gute Tutorials online zu finden scheinen, aber es genügt zu sagen, dass es so etwas wie dies funktioniert:

void iterate(char *str, int count, (^block)(str *)){ 
    for(int i = 0; i < count; i++){ 
    block(list[i]); 
    } 
} 

main() { 
    char str[20]; 
    iterate(str, 20, ^(char c){ 
    printf("%c ", c); 
    }); 

    int accum = 0; 
    iterate(someList, 20, ^(char c){ 
    accum += c; 
    iterate(str, 20, ^(char c){ 
     printf("%c ", c); 
    }); 
    }); 
} 

offensichtlich dieser Code ist sinnlos, aber es druckt es jedes Zeichen einer Zeichenkette (str) mit ein Leerzeichen dazwischen, fügt dann alle Zeichen zusammen zu akkum und druckt jedes Mal die Liste der Zeichen erneut aus.

Hoffe, das hilft. Übrigens, Blöcke sind in Mac OS X Snow Leopard api-s sehr gut sichtbar, und ich glaube, dass sie im kommenden C++ 0x Standard sind, also sind sie nicht wirklich so ungewöhnlich.

+0

Die Lambdas im C++ 1x-Standard unterscheiden sich stark von Apples Block-Erweiterung zu C. Die C++ - Funktion wäre sowieso in C-Kontext nicht sinnvoll. – Chuck

+0

Oh, das habe ich noch nie gesehen ... was ist das Unäre? – Hobblin

+0

Das^ist die Blockdefinitionssyntax. Stellen Sie sich vor, dass es dem Stern in einem Funktionszeiger entspricht, nur für Blöcke. – Chuck

3

Praktisch jede interessante Funktionsanwendung höherer Ordnung erfordert Verschlüsse, was in C die mühsame und fehleranfällige Routine des manuellen Definierens und Füllens von Struct-Funktionsargumenten mit sich bringt.

18

Technisch gesehen sind Funktionen höherer Ordnung nur Funktionen, die Funktionen übernehmen oder zurückgeben. Dinge wie qsort sind also bereits höherrangig.

Wenn Sie etwas mehr wie die Lambda-Funktionen in funktionalen Sprachen (wo Funktionen höherer Ordnung wirklich nützlich werden) bedeuten, sind diese ein bisschen schwieriger und kann nicht natürlich in der aktuellen Standard-C getan werden. Sie sind nur kein Teil der Sprache. Apples Blöcke Erweiterung ist der beste Kandidat. Es funktioniert nur in GCC (und LLVM C-Compiler), aber sie sind wirklich nützlich. Hoffentlich wird sich so etwas durchsetzen. Hier ein paar relevante Ressourcen:

5

Das große Problem bei der Implementierung von Funktionen höherer Ordnung in C ist, dass Sie nicht-triviale Closures ausführen müssen, bei denen es sich um Funktionszeiger mit Datenstrukturen handelt, die lokale Variablen enthalten, auf die sie zugreifen können. Da die Idee hinter Closures darin besteht, lokale Variablen zu erfassen und diese zusammen mit dem Funktionszeiger zu übergeben, ist es schwierig, auf die Compiler-Unterstützung zu verzichten. Und selbst mit der Compiler-Unterstützung ist es schwierig, auf die Garbage-Collection zu verzichten, da Variablen außerhalb ihres Gültigkeitsbereichs existieren können, was es schwierig macht herauszufinden, wann sie freigegeben werden sollen.

0

Dies ist eine Antwort auf die Frage: wie man Funktionen in C zusammenstellt, die hier umgeleitet werden.

Sie können eine Datenstruktur erstellen, um einen Listdatentyp zu implementieren. Diese Struktur kann Funktionszeiger enthalten.

#include<stdlib.h> 
#include<malloc.h> 

typedef (*fun)(); 

typedef struct funList { fun car; struct funList *cdr;} *funList; 

const funList nil = NULL; 

int null(funList fs){ return nil==fs; } 

fun car(funList fs) 
{ 
    if(!null(fs)) return fs->car; 
    else 
    { 
    fprintf(stderr,"error:can't car(nil) line:%d\n",__LINE__); 
    exit(1); 
    } 
} 

funList cdr(funList ls) 
{ if(!null(ls)) return ls->cdr; 
    else 
    { 
    fprintf(stderr,"error:can't cdr(nil) line:%d\n",__LINE__); 
    exit(1); 
    } 
} 

funList cons(fun f, funList fs) 
{ funList ls; 

    ls=(funList) malloc(sizeof(struct funList)); 
    if(NULL==ls) 
    { 
    fprintf(stderr,"error:can't alloc mem for cons(...) line:%d\n",__LINE__); 
    exit(1); 
    } 

    ls->car=f; 
    ls->cdr=fs; 

    return ls; 
} 

können wir eine Funktion comp schreiben, die eine Liste von Funktionen gilt:

type_2 comp(funList fs, type_1 x) 
{ 
    return (null(fs)) ? x : car(fs)(comp(cdr(fs),x)); 
} 

Ein Beispiel, wie es funktioniert.Wir verwenden (FGH) als eine Kurzschreibweise für cons (f, cons (g, cons (h, nil))), die auf ein gegebenes Argument angewandt wird, x:

comp((f g h),x) 

=

f(comp((g h),x)) 

=

f(g(comp((h),x))) 

=

f(g(h(comp(nil,x)))) 

=

f(g(h(x))) 

, wenn Sie die polymorphe Liste Typ in einer typisierten Sprache wie SML oder Haskell die Art der comp hatte sollte sein:

comp :: ([a -> a],a) -> a 

weil in diesem Zusammenhang alle Mitglieder in einer Liste die gleiche haben Art. C kann in diesem Sinne flexibler sein. Vielleicht so etwas wie

typedef void (*fun)(); 

oder

typedef (*fun)(); 

sollten Sie sehen, was die C Handbuch dazu sagen. Und stellen Sie sicher, dass alle zusammenhängenden Funktionen kompatible Typen haben.

Die zu verfassenden Funktionen sollten rein sein, d. H. Ohne Nebenwirkungen oder freie Variablen.