C中的高阶函数

是否有一种“适当的”方式在C中实现更高阶的函数。

我对这里的可移植性和语法正确性等问题非常好奇,如果有多种方法,那么优点和缺点是什么。

编辑:我想知道如何创建更高阶函数的原因是我编写了一个系统来将PyObject列表(在调用python脚本时得到)转换为包含相同数据但以不同方式组织的C结构列表依赖于python.h库。 所以我的计划是有一个函数,它迭代pythonic列表并在列表中的每个项目上调用一个函数,并将结果放在一个列表中然后返回。

所以这基本上是我的计划:

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

并澄清问题:我想知道如何以更安全和更正确的方式做到这一点。我真的想保持更高阶的function风格,但如果这是不赞成的话,我非常感谢以其他方式做到这一点。

如果你热衷于在普通的C中这样做,你需要记住包括从仿函数的调用者(高阶函数)传递一个上下文指针到传入的函数的选项。这可以让你模拟足够的一个关闭,你可以使事情足够容易。 指针指向的……好吧,这取决于你,但它应该是functor的API中的一个void* (或者是它的许多别名之一,例如GLib世界中的gpointer或Tcl C API中的ClientData )。

[编辑]:使用/改编你的例子:

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

这是一个如何做的简单例子,但它应该告诉你的方式。

从技术上讲,高阶函数只是获取或返回函数的函数。 所以像qsort这样的东西已经是高阶的。

如果你的意思更像是在函数式语言中找到的lambda函数(这是高阶函数真正变得有用的地方),那么这些函数要难得多,而且在当前标准C中无法自然完成。它们只是不属于语言。 Apple的区块扩展是最佳候选者。 它只适用于GCC(和LLVM的C编译器),但它们确实非常有用。 希望这样的东西会流行起来。 以下是一些相关资源:

  • 关于该function的Apple文档 (引用了一些Apple特定的技术,并且还解决了Objective-C,但核心块的内容是他们对C的扩展的一部分)
  • 这是一个很好的块介绍
  • Cocoa科学家对C块的概述

在C中实现高阶函数的一个大问题是,要做任何非平凡的事情,你需要闭包,这些函数指针是用包含他们有权访问的局部变量的数据结构扩充的。 因为闭包背后的整个想法是捕获局部变量并将它们与函数指针一起传递,所以没有编译器支持很难做到。 即使有编译器支持,如果没有垃圾收集也很难做到,因为变量可能存在于其范围之外,因此很难确定何时释放它们。

在直接c中,这实际上只是通过函数指针来完成,这既是一种痛苦,也不是这种类型的东西(这部分是为什么它们很痛苦)。 然而,块(或根据非苹果的封闭)非常棒。 他们用gcc-4.x或者其他东西编译,然后用icc编译,但无论你想要什么。 不幸的是,我似乎无法在网上找到任何好的教程,但足以说它的工作原理如下:

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

显然这段代码是没有意义的,但是它打印字符串(str)的每个字符,在它之间有一个空格,然后将所有字符一起添加到accum中,每次它都会再次打印出字符列表。

希望这可以帮助。 顺便说一句,在Mac OS X Snow Leopard api中,块非常明显,我相信它们正处于即将推出的C ++ 0x标准中,所以它们并不是那么不寻常。

实际上任何有趣的高阶函数应用程序都需要闭包,在C中需要手动定义和填充struct函数参数的繁琐且容易出错的例程。

这是一个问题的答案:如何在C中组合函数,这里重定向。

您可以创建数据结构来实现列表数据类型。 该结构可以包含函数指针。

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

我们可以写一个函数comp,它应用了一个函数列表:

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

它是如何工作的一个例子。 我们使用(fgh)作为cons的缩写符号(f,cons(g,cons(h,nil))),它应用于给定的参数x:

 comp((fgh),x) 

=

 f(comp((gh),x)) 

=

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

=

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

=

 f(g(h(x))) 

如果您在SML或Haskell等类型语言中使用了多态列表类型,则comp的类型应为:

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

因为在该上下文中,列表中的所有成员具有相同的类型。 从这个意义上说,C可以更灵活。 也许是这样的

 typedef void (*fun)(); 

要么

 typedef (*fun)(); 

你应该看看C手册对此有何看法。 并确保所有连续的函数都具有兼容的类型。

组成的function应该是纯粹的,即没有副作用,也没有自由变量。

在C语言中很难做到。在C ++中更有可能(参见functors教程或Boost的绑定和函数库)。 最后, C ++ 0x为lambda函数添加了原生支持 ,它可以帮助您捕获函数所依赖的所有变量。

如果您想创建更高阶的函数,请不要使用C.您的问题有C解决方案。 它们可能不优雅,或者你可能会更优雅。

[编辑]我建议实现这一目标的唯一方法是使用脚本语言。 其他人打电话给我。 所以,我用这个替换了这个建议:[/编辑]

你想要达到什么目的? 如果你想模仿闭包,请使用支持它们的语言(你可以通过库绑定Ruby,lua,javascript等)。 如果你想使用回调函数,函数指针就可以了。 函数指针结合了C(指针和弱类型系统)的最危险区域,所以要小心。 函数指针声明也无法读取。

您可以使用函数指针找到一些C库,因为它们必须使用。 如果您正在编写库,也许您也需要使用它们。 如果您只是在自己的代码中使用它们,那么您可能不会在C中思考。您正在考虑使用lisp或scheme或ruby或…并尝试用C语言编写。学习C语言。