-
22-09-2019 - |
题
在 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);
}
并澄清问题:我想知道如何在更安全、更正确的 C 中做到这一点。我真的很想保留高阶函数的风格,但如果这是不受欢迎的,我非常感谢以其他方式做到这一点。
解决方案
如果你是热衷于用标准C这样做,你需要记住,包括从仿函数(高阶函数)来传递的函数的调用者的上下文指针传递的选项。这让您模拟够封闭,你可以做的事情很容易的工作不够。什么是指针指向......好吧,这是你的,但它应该是仿函数的API(或许多别名它,比如在GLib的世界void*
或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 中无法自然完成。它们只是不是语言的一部分。苹果的块扩展是最好的选择。它只适用于 GCC(和 LLVM 的 C 编译器),但它们确实很有用。希望类似的事情能够流行起来。以下是一些相关资源:
- Apple 关于该功能的文档 (参考了一些 Apple 特定的技术,也涉及 Objective-C,但核心块内容是它们对 C 的扩展的一部分)
- 这是关于块的很好的介绍
- 科学家可可对 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-S非常明显的,而且我相信在即将到来的C ++ 0x标准,所以他们并不是真正的不寻常。
实际上任何有趣高阶函数应用需要封闭件,其用C需要的手动定义和填充结构函数的参数laborous且容易出错的例行程序。
这是一个问题的答案:如何编写在C,其在此重定向功能
可以创建一个数据结构来实现的列表的数据类型。 该结构可以包含函数指针。
#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;
}
我们可以写一个函数comp施加的函数的列表:
type_2 comp(funList fs, type_1 x)
{
return (null(fs)) ? x : car(fs)(comp(cdr(fs),x));
}
它是如何工作的例子。我们用(F G H),为短符号用于利弊(F,缺点(克,利弊(H,无))),其被施加到给定自变量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)))
如果你有像SML或哈斯克尔类型化的语言使用的多态列表类型比较的类型应该是:
comp :: ([a -> a],a) -> a
,因为在这种情况下列表中的所有成员具有相同的类型。 C可在这个意义上更加灵活。可能像
typedef void (*fun)();
或
typedef (*fun)();
您应该看到关于这个的C语言手册发言权。并确保所有连续函数有兼容的类型。
要撰写的功能应该是纯的,即没有副作用,也没有自由变量。
在直C中很难做到。在 C++ 中更可能(参见 函子教程 或升压的 绑定 和 功能 图书馆)。最后, C++0x 添加了对 lambda 函数的本机支持, ,它负责在闭包中捕获函数所依赖的所有变量。
如果您想要创建更高级的功能,不使用C,有是C解决您的问题。他们可能不优雅,或者他们可能会更优雅,你知道。
[编辑]我建议实现此目标的唯一途径是使用脚本语言。也有人叫我出去就可以了。所以,我替换该建议与此:[/编辑]
什么是你想达到什么目的?如果你想模仿关闭,使用一种语言,支持他们(你可以绑定到红宝石,Lua中的JavaScript等通过库)。如果你想使用回调函数,函数指针确定。函数指针结合C(指针和弱类型系统)最危险的地方,所以要小心。函数指针声明不阅读的乐趣,要么。
您发现使用函数指针,因为他们有一些C库。如果你正在编写一个库,也许你需要使用它们了。如果你只是使用他们自己的代码中,你很可能不会在C.思考你在Lisp或计划或红宝石或思考......并试图把它写在C.了解的C方式。