Frage

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

Ich bin meistens neugierig über Dinge wie Portabilität und Syntax Korrektheit hier und wenn es mehr als eine Art und Weise, was die Vorzüge und Mängel sind.

Edit: Der Grund, warum ich mag wissen, wie Funktionen höhere Ordnung zu schaffen sind, dass ich ein System geschrieben habe PyObject Listen zu konvertieren (die Sie erhalten, wenn Python-Skripte aufrufe) in eine Liste von C-Strukturen der gleichen Daten enthalten, aber in einer Art und Weise nicht abhängig organisiert auf die python.h Bibliotheken. Also mein Plan ist es, eine Funktion zu haben, die eine pythonic Liste durchlaufen und rufen eine Funktion auf jedes Element in der Liste, und das Ergebnis in einer Liste, die sie dann zurück.

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 dies in sicherer machen und korrektere C. Ich würde wirklich wie die Funktion höherer Ordnung Stil zu halten, aber wenn das bei mir sehr verpönt schätzen Möglichkeiten, dies eine andere zu tun Art und Weise.

War es hilfreich?

Lösung

Wenn Sie daran interessiert, auf tun dies in Ebene C, müssen Sie daran denken, die Option, um in einem Kontextzeiger von dem Anrufer des Funktors (die Funktion höherer Ordnung) an die Funktion übergeben zu übergeben. Auf diese Weise können Sie simulieren genug einen Verschluss, dass man die Dinge einfach genug machen arbeiten. Was das Zeiger zeigt auf ... na ja, das liegt an dir, aber es sollte ein void* in der Funktors API (oder eine der vielen Aliase für sie, wie gpointer in der GLib Welt oder ClientData in der Tcl-C-API) sein .

[EDIT]: Zur Verwendung / 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.

Andere Tipps

Technisch Funktionen höherer Ordnung sind nur Funktionen, die oder Rückkehr Funktionen übernehmen. So Dinge wie qsort sind bereits höhere Ordnung.

Wenn Sie meinen, etwas mehr wie die Lambda-Funktionen in funktionalen Sprachen gefunden (wo Funktionen höhere Ordnung wirklich nützlich werden), das ist ein bisschen härter und kann nicht natürlich in der aktuellen Standard-C durchgeführt werden Sie sind einfach nicht Teil der Sprache. Apple Blöcke Erweiterung ist der beste Kandidat. Es funktioniert nur in GCC (und LLVM der C-Compiler), aber sie sind wirklich nützlich. Hoffentlich so ähnlich wird fangen. Hier ein paar relevanten Informationen:

Das große Problem mit Funktionen höherer Ordnung in C Implementierung ist, dass irgendetwas nicht-trivial Sie brauchen Verschlüsse zu tun, die Funktionszeiger sind ergänzt mit Datenstrukturen lokale Variablen sie Zugriff haben. Da die ganze Idee hinter Verschlüssen lokale Variablen erfassen und die zusammen mit dem Funktionszeiger übergibt, ist es schwer, ohne Compiler-Unterstützung zu tun. Und selbst mit Compiler-Unterstützung ist es schwer, ohne Garbage Collection zu tun, weil Variablen außerhalb ihres Umfangs vorhanden ist, macht es schwer, herauszufinden, wenn sie zu befreien.

In gerader, das ist wirklich nur durch Funktionszeiger gemacht, die sowohl ein Schmerz sind und nicht für diese Art der Sache gemeint (die teilweise ist, warum sie ein Schmerz sind). Blöcke (oder Verschlüsse, nach Nicht-Apfel) sind für diese fantastische, though. Sie kompilieren in gcc-4.x oder so etwas, und icc etwas, aber egal das ist, was Sie suchen. Leider kann ich keine gute Tutorials scheinen online zu finden, 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 er druckt es jedes Zeichen einer Zeichenkette (str) mit einem Raum dazwischen sie, fügt dann alle Zeichen zusammen in accum, und jedesmal, wenn sie es tut druckt die Liste der Zeichen wieder.

Hope, das hilft. By the way, sind Blöcke sehr sichtbar in Mac OS X Snow Leopard api-s, und ich glaube, sind in der kommenden C ++ 0x-Standard, so sind sie nicht wirklich so ungewöhnlich.

praktisch jede interessante Funktion höhere Ordnung Anwendung erfordert Verschlüsse, die in der C laborous und fehleranfällige bringen Routine von manuell zu definieren und struct Funktionsargumente füllen.

Dies ist eine Antwort auf die Frage:., Wie in C zu schreiben Funktionen, die hier umgeleitet

Sie können eine Datenstruktur erstellen eine Liste Datentyp zu implementieren. dass Strukturfunktionszeiger enthalten kann.

#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 (f g h) als Kurzschreibweise für cons (f, Nachteile (g, cons (h, nil))), die auf ein bestimmtes Argument x angewendet wird:

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 sein sollte:

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

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

typedef void (*fun)();

oder

typedef (*fun)();

Sie sollten sehen, was die C-Handbuch sagen darüber. Und achten Sie darauf, dass alle zusammenhängenden Funktionen kompatible Typen haben.

Die Funktionen compose sollte rein sein, das heißt ohne Nebenwirkungen noch freie Variablen.

Es ist sehr schwierig, in gerade C. zu tun Es ist mehr möglich, in C ++ (siehe functors Tutorial oder Boost- binden und Funktion Bibliotheken). Schließlich C ++ 0x fügt native Unterstützung für Lambda-Funktionen , die für Sie kümmert sich um alle Variablen in Schließung des Erfassens, dass Ihr funcion abhängt.

Wenn Sie Funktionen höherer Ordnung erstellen möchten, verwenden Sie nicht C. Es gibt C-Lösungen für Ihr Problem. Sie dürfen nicht elegant sein, oder sie können elegantere, dass Sie erkennen.

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

Was wollen Sie erreichen? Wenn Sie zu imitieren Schließungen möchten, verwenden Sie die Sprache, die sie unterstützt (Sie in Rubin binden kann, lua, Javascript, etc durch Bibliotheken). Wenn Sie Rückrufe verwenden möchten, sind Funktionszeiger ok. Funktionszeiger die gefährlichsten Gebiete von C (Zeiger und dem schwachen Typ-System) kombinieren, so vorsichtig sein. Funktionszeiger Erklärungen sind nicht Spaß zu lesen, auch nicht.

Sie finden einige C-Bibliotheken mit Funktionszeigern, weil sie müssen. Wenn Sie eine Bibliothek schreiben, vielleicht brauchen Sie, sie zu benutzen, auch. Wenn Sie sich nur um sie in Ihrem eigenen Code verwenden, denken Sie wahrscheinlich nicht in C. Sie denken in Lisp oder Schema oder Rubin oder ... und versuchen, es in C zu schreiben, um die C Art und Weise lernen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top