C中的函数式编程有哪些工具?

时间:2008-10-19 05:12:18

标签: c functional-programming

我最近一直在思考如何在C语言中进行函数式编程( not C ++)。显然,C是一种过程语言,本身并不真正支持函数式编程。

是否有任何编译器/语言扩展为该语言添加了一些函数式编程结构? GCC提供nested functions作为语言扩展;嵌套函数可以从父堆栈框架中访问变量,但距离成熟的闭包还有很长的路要走。

例如,我认为在C中真正有用的一件事是,在任何需要函数指针的地方,你都可以传递一个lambda表达式,创建一个衰变为函数指针的闭包。 C ++ 0x将包含lambda表达式(我觉得很棒);但是,我正在寻找适用于直接C的工具。

[编辑]为了澄清,我不是试图解决C中更适合函数式编程的特定问题;如果我愿意的话,我只是想知道那里有什么工具。

13 个答案:

答案 0 :(得分:83)

你可以使用GCC的嵌套函数来模拟lambda表达式,事实上,我有一个宏来为我做这个:

#define lambda(return_type, function_body) \
  ({ \
    return_type anon_func_name_ function_body \
    anon_func_name_; \
  })

像这样使用:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });

答案 1 :(得分:54)

函数式编程不是关于lambdas,而是关于纯函数。所以以下广泛推广功能风格:

  1. 仅使用函数参数,不要使用全局状态。

  2. 最大限度地减少副作用,即printf或任何IO。返回描述可以执行的IO的数据,而不是直接在所有函数中引起副作用。

  3. 这可以通过简单的c来实现,不需要魔术。

答案 2 :(得分:38)

FFCALL允许您在C中构建闭包 - callback = alloc_callback(&function, data)返回一个函数指针,使callback(arg1, ...)等同于调用function(data, arg1, ...)。但是,您必须手动处理垃圾收集。

相关地,blocks已添加到Apple的GCC分支中;它们不是函数指针,但是它们允许你传递lambdas,同时避免需要手动构建和释放捕获变量的存储空间(实际上,一些复制和引用计数发生,隐藏在一些语法糖和运行时库之后)。 p>

答案 3 :(得分:17)

Hartel& Muller的书 Functional C ,现在可以在http://eprints.eemcs.utwente.nl/1077/找到(2012-01-02)(有PDF版本的链接)。

答案 4 :(得分:7)

想到的主要事情是使用代码生成器。您是否愿意使用提供函数式编程的不同语言进行编程,然后从中生成C代码?

如果这不是一个有吸引力的选择,那么你可以滥用CPP来获得部分方式。宏系统应该让您模拟一些函数式编程思想。我听说过gcc是以这种方式实现的,但我从未检查过。

C当然可以使用函数指针传递函数,主要问题是缺少闭包,类型系统往往会妨碍。您可以探索比CPP更强大的宏系统,例如M4。我想最终,我建议的是,如果没有很大的努力,真正的C不能完成任务,但你可以扩展C以使其完成任务。如果您使用CPP,那么该扩展看起来最像C,或者您可以转到频谱的另一端并从其他语言生成C代码。

答案 5 :(得分:5)

如果你想实现闭包,你将不得不使用汇编语言和堆栈交换/管理。不建议反对它,只是说这是你必须做的。

不确定如何在C中处理匿名函数。在冯·诺依曼机器上,你可以在asm中执行匿名函数。

答案 6 :(得分:4)

答案 7 :(得分:4)

函数式编程风格的先决条件是一流的功能。 如果您容忍下一个,可以在便携式C中进行模拟:

  • 手工管理词法范围绑定,又称关闭。
  • 手动管理功能变量的生命周期。
  • 函数application / call的替代语法。
/* 
 * with constraints desribed above we could have
 * good approximation of FP style in plain C
 */

int increment_int(int x) {
  return x + 1;
}

WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS(increment, increment_int);

map(increment, list(number(0), number(1)); // --> list(1, 2)


/* composition of first class function is also possible */

function_t* computation = compose(
  increment,
  increment,
  increment
);

*(int*) call(computation, number(1)) == 4;

此类代码的运行时可能小到下面的一个

struct list_t {
  void* head;
  struct list_t* tail;
};

struct function_t {
   void* (*thunk)(list_t*);
   struct list_t* arguments;
}

void* apply(struct function_t* fn, struct list_t* arguments) {
  return fn->thunk(concat(fn->arguments, arguments));
}

/* expansion of WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS */
void* increment_thunk(struct list_t* arguments) {
  int x_arg = *(int*) arguments->head;
  int value = increment_int(x_arg);
  int* number = malloc(sizeof *number);

  return number ? (*number = value, number) : NULL;
}

struct function_t* increment = &(struct function_t) {
  increment_thunk,
  NULL
};

/* call(increment, number(1)) expands to */
apply(increment, &(struct list_t) { number(1), NULL });

本质上我们模仿第一类函数,闭包表示为一对函数/参数加上一堆宏。可以找到完整的代码here

答案 8 :(得分:1)

很多编程语言都是用C语言编写的。其中一些语言支持作为一等公民的功能,该领域的语言是ecl(嵌入式常见的lisp IIRC),Gnu Smalltalk(gst)(Smalltalk有块),然后有“闭包”的库,例如glib2 http://library.gnome.org/devel/gobject/unstable/chapter-signal.html#closure 至少接近功能编程。因此,使用其中一些实现进行函数式编程可能是一种选择。

嗯,或者你可以去学习Ocaml,Haskell,Mozart / Oz等; - )

此致

答案 9 :(得分:1)

Felix language编译为C ++。如果你不介意C ++,也许这可能是一个步骤。

答案 10 :(得分:0)

我在C中进行函数式编程的方法是在C中编写一个函数式语言解释器。我将它命名为Fexl,它是"函数表达式语言的简称。"

解释器非常小,在启用-O3的系统上编译为68K。它也不是玩具 - 我将它用于我为我的业务编写的所有新生产代码(基于网络的投资合伙企业会计。)

现在我只编写C代码(1)添加一个调用系统例程的内置函数(例如fork,exec,setrlimit等),或者(2)优化一个函数,否则可以用Fexl编写(例如,搜索子字符串)。

模块机制基于" context"的概念。上下文是一个函数(用Fexl编写),它将符号映射到它的定义。当您阅读Fexl文件时,您可以使用您喜欢的任何上下文来解决它。这允许您创建自定义环境,或在受限制的沙盒中运行代码。"

http://fexl.com

答案 11 :(得分:-4)

你想要使C功能,语法或语义成为什么?函数式编程的语义当然可以添加到C编译器中,但是当你完成时,你基本上已经拥有了现有的一种函数语言,比如Scheme,Haskell等。

更好地利用时间来学习那些直接支持这些语义的语言的语法。

答案 12 :(得分:-4)

不了解C.目前在Objective-C中有一些功能特性,OSX上的GCC也支持一些功能,但我再次建议开始使用功能语言,上面提到了很多。我个人从计划开始,有一些优秀的书籍,如The Little Schemer,可以帮助你这样做。