Pregunta

¿Hay una manera "correcta" para implementar funciones de orden superior en C.

Lo que más me curiosidad por cosas como la portabilidad y la sintaxis correcta aquí y si hay más de una forma de lo que los méritos y defectos son.

Editar: La razón por la que quieren saber cómo crear funciones de orden superior son lo que he escrito un sistema para convertir las listas PyObject (que se obtiene cuando se llama a los scripts de Python) en una lista de estructuras de C que contengan los mismos datos, pero organizado de una manera que no depende de las bibliotecas Python.h. Así que mi plan es tener una función que itera a través de una lista de Pythonic y llama a una función en cada elemento de la lista y coloca el resultado en una lista que luego vuelve.

Así que esto es básicamente mi 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);
}

Y clearify la pregunta: Quiero saber cómo hacer esto en más seguro y más correcta C. Realmente me gustaría mantener el estilo función de orden superior, pero si eso es mal visto que agradecería enormemente maneras de hacer esto en otro camino.

¿Fue útil?

Solución

Si usted está interesado en hacer esto en C plano, es necesario recordar para incluir la opción de pasar un puntero en el contexto de la persona que llama de la funtor (la función de orden superior) a la función se ha pasado. De este modo, se simula lo suficiente de un cierre que se puede hacer funcionar las cosas con bastante facilidad. Lo que apunta el puntero a ... bueno, eso depende de usted, pero debe ser un void* en la API del funtor (o uno de los muchos alias para ello, tales como gpointer en el mundo GLib o ClientData en la API Tcl C) .

[EDIT]: Para uso / adaptar su ejemplo:

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

Este es un ejemplo trivial de cómo hacerlo, pero debe mostrar el camino.

Otros consejos

Técnicamente, funciones de orden superior son funciones que se llevan a solo o funciones devuelven. Así las cosas, como ya se qsort son de orden superior.

Si usted se refiere a algo más parecido a las funciones lambda encuentran en los lenguajes funcionales (que es donde realmente funciones de orden superior se convierten en útil), los que son un poco más duro y no se puede hacer de forma natural en la corriente estándar C. Son sólo no forma parte de la lengua. bloques de extensión de Apple es el mejor candidato. Sólo funciona en GCC (y LLVM compilador de C), pero son realmente útiles. Esperemos que algo así se pongan al día. He aquí algunos recursos relevantes:

El gran problema con la implementación de funciones de orden superior en C es que para hacer algo no trivial que necesita cierres, que son punteros a funciones aumentadas con las estructuras de datos que contienen las variables locales que tienen acceso. Dado que la idea detrás de los cierres es capturar las variables locales y pasar los junto con el puntero de función, que es difícil de hacer sin el apoyo del compilador. E incluso con el apoyo del compilador es difícil de hacer sin recolección de basura ya que las variables pueden existir fuera de su alcance, por lo que es difícil saber cuándo liberarlos.

c recta, esto es realmente sólo se realiza a través de punteros de función, que son a la vez un dolor y no significaba para este tipo de cosas (que es parcialmente por qué son un dolor). Blocks (o dispositivos de cierre, de acuerdo con no Apple) son fantásticas para esto, sin embargo. Recopilan en gcc-4.x o algo así, y el ICC algo, pero sin tener en cuenta eso es lo que estás buscando. Por desgracia, me parece que no puede encontrar tutoriales en línea buena, pero basta con decir que funciona algo como esto:

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

obviamente, este código no tiene sentido, pero se imprime cada carácter de una cadena (str) con un espacio entre ellos que, a continuación, agrega todos los personajes juntos en acum, y cada vez que lo hace imprime la lista de caracteres de nuevo.

Espero que esto ayude. Por cierto, los bloques son muy visibles en Mac OS X Snow Leopard API-s, y creo que están en la próxima C ++ 0x estándar, así que no son realmente tan inusual.

Prácticamente cualquier aplicación función de orden superior interesante requiere dispositivos de cierre, que en C conlleva la rutina laborous y propenso a errores de definir y llenar argumentos de la función struct manualmente.

Esta es una respuesta a la pregunta:. Cómo componer funciones en C, que se redirige aquí

Se puede crear una estructura de datos para implementar un tipo de lista de datos. que la estructura puede contener punteros de función.

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

podemos escribir una función comp que se aplica una lista de funciones:

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

Un ejemplo de cómo funciona. Utilizamos (f g h) como un corto notación para los contras (F, contras (g, contras (h, nil))), que se aplica a un argumento dado 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)))

si se hubiera usado el tipo de lista polimórficos en una lengua escrita a máquina o como SML Haskell del tipo de borrador debe ser:

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

debido a que en ese contexto todos los miembros de una lista tienen el mismo tipo. C puede ser más flexible en este sentido. Tal vez algo como

typedef void (*fun)();

o

typedef (*fun)();

debería ver lo que dicen los manuales C sobre esto. Y asegúrese de que todas las funciones contiguas tienen tipos compatibles.

Las funciones para componer debe ser puro, es decir, sin efectos secundarios ni variables libres.

Es muy difícil de hacer en recta C. Es más posible en C ++ (ver funtores tutorial o Boost unen y bibliotecas de funciones ). Por último, C ++ 0x añade soporte nativo para las funciones lambda , que se encarga de capturar para usted en el cierre de todas las variables que su funcion depende.

Si desea crear funciones de orden superior, no use C. Hay soluciones C a su problema. Puede que no sean elegante, o pueden ser más elegante que se den cuenta.

[Editar] me sugirió que la única forma de lograrlo era utilizar un lenguaje de script. Otros me han llamado a cabo en él. Por lo tanto, yo estoy reemplazando esa sugerencia con esto: [/ Editar]

¿Qué estás tratando de lograr? Si desea cierres imitan, use un lenguaje compatible con ellos (se puede atar en Rubí, lua, Javascript, etc a través de bibliotecas). Si desea utilizar devoluciones de llamada, los punteros de función están bien. punteros de función se combinan las zonas más peligrosas de C (punteros y el sistema de tipo débil), así que ten cuidado. declaraciones de puntero de función no son divertidos de leer, ya sea.

encontrar algunas librerías C utilizando punteros de función porque tienen que hacerlo. Si estás escribiendo una biblioteca, tal vez usted necesita utilizarlos, también. Si acaba de usarlos dentro de su propio código, probablemente no estás pensando en C. usted está pensando en Lisp o esquema o rubí o ... y tratando de escribirlo en C. Aprender la forma C.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top