2010-03-29 11 views
16

¿Hay una manera "correcta" para implementar funciones de orden superior en C.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 lo que las los méritos y los defectos son Edit: La razón por la que quiero saber cómo crear funciones de orden superior es que he escrito un sistema para convertir listas de PyObject (que se obtienen al llamar scripts de Python) en una lista de estructuras C que contienen 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 itere a través de una lista pitonica y llame a una función en cada elemento de la lista y coloque el resultado en una lista que luego devuelve.

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 C más seguro y más correcto Realmente me gustaría mantener el estilo función de orden superior pero si eso no está bien visto, aprecio mucho las maneras de hacerlo de otra manera.

+1

Buena pregunta, pero vaga. Parece que hiciste una investigación sobre esto. Considera actualizar tu pregunta para indicar lo que ya has encontrado. Las respuestas dadas son geniales, pero solo pueden ser tan específicas como la pregunta. –

Respuesta

4

Si está interesado en hacer esto en C simple, debe recordar incluir la opción de pasar un puntero de contexto de la persona que llama del functor (la función de orden superior) a la función pasada. Esto le permite simular lo suficiente de un cierre que puede hacer que las cosas funcionen con la suficiente facilidad. A lo que apunta ese puntero ... bueno, eso depende de usted, pero debería ser un void* en la API del functor (o uno de los muchos alias para él, como gpointer en el mundo GLib o ClientData en la API Tcl C) .

[EDIT]: Para utilizar/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.

+0

Gracias :) Esa es una muy buena respuesta. – Hobblin

0

Si desea crear funciones de orden superior, no use C. Hay soluciones C para su problema. Puede que no sean elegantes, o pueden ser más elegantes de lo que piensas.

[Editar] Sugerí que la única forma de lograr esto era utilizar un lenguaje de scripting. Otros me han llamado a eso. Entonces, estoy reemplazando esa sugerencia con esto: [/ Edit]

¿Qué estás tratando de lograr? Si desea imitar cierres, use un lenguaje que los admita (puede vincularse con Ruby, lua, javascript, etc. a través de las bibliotecas). Si desea utilizar devoluciones de llamada, los punteros a las funciones están bien. Los punteros de función combinan las áreas más peligrosas de C (punteros y el sistema de tipo débil), así que ten cuidado. Las declaraciones de puntero a la función tampoco son divertidas de leer.

Encuentra algunas bibliotecas C que utilizan punteros a las funciones porque tienen que hacerlo. Si está escribiendo una biblioteca, tal vez necesite usarlos también. Si solo los estás usando dentro de tu propio código, probablemente no estás pensando en C. Estás pensando en lisp o en esquema o ruby ​​o ... y tratando de escribirlo en C. Aprende la forma de C.

+0

Las funciones de orden superior son "meras" funciones que toman una o varias funciones como argumentos o devuelven una función. Todo esto se puede hacer en C, usando punteros para funcionar. Se cita el "meramente" porque si bien la mecánica de esto puede ser relativamente simple (aunque de manera más o menos natural y extensiva con el apoyo de diferentes idiomas) el uso de funciones de orden superior puede conducir a una programación muy poderosa y de doblez mental. – mjv

+1

Orden superior! = Código dinámico. Una función de orden superior es simplemente una función que opera sobre funciones, que es perfectamente realizable en C. Un algoritmo de clasificación con una función de comparación conectable es una función de orden superior. Un integrador numérico es Un registro de devolución de llamada con temporizador es. El principal desafío es que tienes que aprender la sintaxis, que es bastante diferente a cualquier otra cosa en el idioma. –

+0

Lo que Daniel y mjv dijeron ... – Hobblin

4

En la recta c, esto solo se realiza a través de punteros a funciones, que son a la vez un dolor y no para este tipo de cosas (que es en parte por qué son un dolor). Sin embargo, los bloques (o los cierres, según el no-manzana) son fantásticos para esto. Compilan en gcc-4.x o algo así, y icc algo, pero independientemente de eso es lo que estás buscando. Por desgracia, me parece que no puede encontrar buenos tutoriales en línea, 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, luego agrega todos los caracteres juntos en accum, y cada vez que lo hace imprime la lista de caracteres nuevamente.

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 el próximo estándar C++ 0x, por lo que no son tan inusuales.

+0

Las lambdas en el estándar C++ 1x son muy diferentes de la extensión de bloques de Apple a C. La característica C++ no sería realmente significativa en un contexto C de todos modos. – Chuck

+0

Oh, ese que nunca he visto ... ¿qué es el^unario? – Hobblin

+0

La^es la sintaxis de definición de bloque. Piénselo como equivalente a la estrella en un puntero de función, solo para bloques. – Chuck

3

Prácticamente cualquier aplicación de función de orden superior interesante requiere cierres, lo que en C implica la rutina laboriosa y propensa a errores de definir y rellenar manualmente los argumentos de la función struct.

18

Técnicamente, las funciones de orden superior son solo funciones que toman o devuelven funciones. Entonces cosas como qsort ya son de orden superior.

Si se refiere a algo más como las funciones lambda encontradas en los lenguajes funcionales (que es donde las funciones de orden superior realmente se vuelven útiles), son bastante más difíciles y no se pueden hacer naturalmente en el estándar actual C. Son simplemente no es parte del lenguaje. La extensión de bloques de Apple es el mejor candidato. Solo funciona en GCC (y en el compilador C de LLVM), pero son realmente útiles. Con suerte, algo así se pondrá de moda. Aquí hay algunos recursos relevantes:

5

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

0

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

Puede crear una estructura de datos para implementar un tipo de datos de lista. esa estructura puede contener indicadores 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 (fgh) 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 un lenguaje escrito como LME o Haskell del tipo de borrador debe ser:

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

porque 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 el manual C decir acerca de esto. Y asegúrese de que todas las funciones contiguas tengan tipos compatibles.

Las funciones para componer deben ser puras, es decir, sin efectos secundarios ni variables libres.

Cuestiones relacionadas