162

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

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

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

[编辑] 澄清一下,我并不是要解决 C 中更适合函数式编程的特定问题。如果我想这样做,我只是好奇那里有什么工具。

4

13 回答 13

95

可以使用 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; });
于 2010-07-31T14:31:01.130 回答
78

函数式编程不是关于 lambda,而是关于纯函数。所以下面大致推广功能风格:

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

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

这可以在纯 c 中实现,不需要魔法。

于 2013-11-08T23:22:57.760 回答
42

FFCALL允许您在 C 中构建闭包——callback = alloc_callback(&function, data)返回一个函数指针,这callback(arg1, ...)相当于调用function(data, arg1, ...)。不过,您将不得不手动处理垃圾收集。

与此相关的是,Apple 的 GCC 分支中已添加了块;它们不是函数指针,但它们让您可以传递 lambda,同时避免手动为捕获的变量构建和释放存储空间(实际上,发生了一些复制和引用计数,隐藏在一些语法糖和运行时库后面)。

于 2008-10-19T18:32:19.403 回答
18

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

于 2012-01-02T05:35:36.580 回答
12

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

  • 词法范围绑定的手动管理,也就是闭包。
  • 手动管理函数变量的生命周期。
  • 函数应用程序/调用的替代语法。
/* 
 * 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 });

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

于 2017-08-16T17:05:47.007 回答
7

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

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

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

于 2008-10-19T05:24:49.710 回答
5

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

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

于 2008-10-21T14:02:31.693 回答
4

我在 C 中进行函数式编程的方法是用 C 编写函数式语言解释器。我将其命名为 Fexl,它是“函数表达式语言”的缩写。

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

现在我编写 C 代码只是为了 (1) 添加一个调用系统例程的内置函数(例如 fork、exec、setrlimit 等),或 (2) 优化一个原本可以用 Fexl 编写的函数(例如 search对于子字符串)。

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

http://fexl.com

于 2018-03-31T23:16:30.237 回答
3

看看 Hartel & Muller 的书,Functional C

http://www.ub.utwente.nl/webdocs/ctit/1/00000084.pdf
http://www.cs.bris.ac.uk/~henkm/f2c/index.html

于 2008-10-21T06:49:41.677 回答
2

Felix 语言编译为 C++ 。如果您不介意 C++,也许这可能是一个垫脚石。

于 2010-11-04T13:42:04.590 回答
1

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

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

问候

于 2008-10-19T06:02:26.513 回答
-2

你想让 C 函数化,语法或语义是什么?函数式编程的语义当然可以添加到 C 编译器中,但是当你完成时,你基本上已经拥有了现有函数式语言之一的等价物,例如 Scheme、Haskell 等。

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

于 2008-10-19T07:54:42.933 回答
-3

不了解 C。虽然 Objective-C 中有一些功能特性,OSX 上的 GCC 也支持一些特性,但是我再次建议开始使用函数式语言,上面提到了很多。我个人是从计划开始的,有一些很棒的书,比如 The Little Schemer 可以帮助你做到这一点。

于 2009-11-29T22:36:49.737 回答