Domanda

C'è un modo "corretto" per implementare le funzioni di ordine superiore in C.

Sono per lo più curioso di cose come la portabilità e la sintassi correttezza qui e se ci sono più di uno modi quello che i pregi ei difetti sono.

Modifica: Il motivo che voglio sapere come creare funzioni di ordine superiore sono che ho scritto un sistema per convertire le liste PyObject (che si ottiene quando si chiama script python) in una lista di strutture C che contengono gli stessi dati, ma organizzata in modo non dipendente le librerie Python.h. Quindi il mio piano è quello di avere una funzione che itera attraverso una lista divinatorio e chiede una funzione su ciascun elemento della lista e pone il risultato in una lista che restituisce poi.

Quindi, questo è fondamentalmente il mio piano:

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

E per clearify alla domanda: voglio sapere come fare questo nel più sicuro e più corretto C. Mi piacerebbe davvero mantenere lo stile funzione di ordine superiore, ma se questo è visto di buon occhio Apprezzo molto modi per fare questo qualche altro modo.

È stato utile?

Soluzione

Se siete appassionati di fare questo in C puro, è necessario ricordarsi di includere l'opzione di passare in un puntatore di contesto dal chiamante del funtore (la funzione di ordine superiore) per la funzione passata. In questo modo, si simula abbastanza di una chiusura che si possono fare le cose funzionano abbastanza facilmente. Quali punti che puntatore a ... beh, questo sta a voi, ma dovrebbe essere un void* nel API del funtore (o uno dei tanti alias per esso, come ad esempio gpointer nel mondo GLib o ClientData nelle API Tcl C) .

[EDIT]: per utilizzare / adattare il vostro esempio:

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

Questo è un esempio banale di come farlo, ma dovrebbe mostrarvi la strada.

Altri suggerimenti

Tecnicamente, funzioni di ordine superiore sono solo le funzioni che assumono o funzioni restituiscono. Quindi le cose come qsort sono già di ordine superiore.

Se vuoi dire qualcosa di più come le funzioni lambda trovano in linguaggi funzionali (che è dove le funzioni di ordine superiore diventano veramente utile), questi sono un po 'più duro e non può essere fatto naturalmente in standard attuale C. Sono solo non fa parte del linguaggio. blocca l'estensione di Apple è il miglior candidato. Funziona solo nel GCC (e LLVM del compilatore C), ma sono veramente utili. Speriamo che qualcosa del genere si adatteranno. Ecco alcune risorse rilevanti:

Il grosso problema di attuare funzioni di ordine superiore in C è quello di fare qualcosa di non banale è necessario chiusure, che sono puntatori a funzione aumentata con strutture di dati contenenti le variabili locali che hanno accesso. Dal momento che l'intera idea alla base di chiusure è di catturare le variabili locali e superare quelli con il puntatore a funzione, è difficile da fare senza il supporto del compilatore. E anche con il supporto del compilatore è difficile fare a meno di garbage collection, perché le variabili possono esistere al di fuori della loro portata, rendendo difficile capire quando di liberarli.

In c dritto, questo è davvero fatto solo attraverso puntatori a funzione, che sono sia un dolore e non significava per questo tipo di cose (che è in parte il motivo per cui sono un dolore). Blocchi (o chiusure, secondo non Apple) sono fantastici per questo, però. Essi elaborati gcc-4.x o qualcosa del genere, e ICC qualcosa, ma questo è a prescindere quello che stai cercando. Purtroppo, non riesco a trovare nessuna buoni tutorial on-line, ma basti dire che funziona in questo modo:

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

, ovviamente, questo codice è inutile, ma esso stampa ogni carattere di una stringa (str) con uno spazio in mezzo, poi aggiunge tutti i personaggi insieme in accum, e ogni volta che lo fa esso stampa l'elenco dei caratteri ancora una volta.

Spero che questo aiuti. Tra l'altro, i blocchi sono molto visibili in Mac OS X Snow Leopard API-s, e credo che si trovano nella prossima serie C ++ 0x, quindi non sono poi così insolito.

Praticamente qualsiasi applicazione funzione di ordine superiore interessa richiede dispositivi di chiusura, che in C comporta routine laborous e soggetto ad errori di definire e riempire argomenti della funzione struct manualmente.

Questa è una risposta alla domanda:. Come comporre le funzioni in C, che viene reindirizzato qui

È possibile creare una struttura di dati per implementare un tipo di elenco di dati. che la struttura può contenere puntatori a funzione.

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

siamo in grado di scrivere una funzione bozzetto che si applica un elenco di funzioni:

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

Un esempio di come funziona. Usiamo (f g h) come una breve notazione per cons (f, g, cons (cons (h, nil))), che è applicato ad un dato argomento 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)))

se si fosse usato il tipo di elenco polimorfico in un linguaggio tipizzato come SML o Haskell del tipo di comp dovrebbe essere:

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

perché in tale contesto tutti i membri di una lista hanno lo stesso tipo. C può essere più flessibile in questo senso. Forse qualcosa di simile

typedef void (*fun)();

o

typedef (*fun)();

si dovrebbe vedere ciò che il manuale C dire su questo. Ed essere sicuri che tutte le funzioni contigue hanno tipi compatibili.

Le funzioni per comporre dovrebbero essere puro, cioè senza effetti collaterali né variabili libere.

E 'molto difficile da fare in C. E' più diritta possibile in C ++ (vedi funtori esercitazione o di Boost href="http://www.boost.org/doc/libs/1_42_0/libs/bind/bind.html" rel="nofollow noreferrer"> legano e function librerie). Infine, C ++ 0x aggiunge il supporto nativo per le funzioni lambda , che si prende cura per voi di catturare in chiusura tutte le variabili che il vostro funcion dipende.

Se si desidera creare funzioni di ordine superiore, non utilizzare C. Ci sono soluzioni C al problema. Essi non possono essere eleganti, o possono essere più elegante che ci si rende conto.

[Modifica] Ho suggerito che l'unico modo per raggiungere questo obiettivo è stato quello di utilizzare un linguaggio di scripting. Altri mi hanno chiamato su di esso. Così, sto sostituendo quel suggerimento con questo: [/ Modifica]

Che cosa stai cercando di raggiungere? Se si vuole imitare le chiusure, utilizzare un linguaggio che li sostiene (si può legare in Ruby, Lua, javascript, ecc attraverso le biblioteche). Se si desidera utilizzare i callback, puntatori a funzione sono ok. puntatori a funzione combinano le zone più pericolose della C (puntatori e il sistema di tipo debole), quindi state attenti. dichiarazioni puntatore a funzione non sono divertenti da leggere, sia.

Ci trovi alcune librerie C usando puntatori a funzione perché devono. Se si sta scrivendo una libreria, forse avete bisogno di usarli, anche. Se si sta solo utilizzando il proprio codice all'interno, probabilmente non state pensando in C. Stai pensando in Lisp o regime o rubino o ... e cercando di scrivere in C. Imparare il modo C.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top