162

私は最近、C(C ++ではなく)で関数型プログラミングを行う方法について多くのことを考えています。明らかに、Cは手続き型言語であり、関数型プログラミングをネイティブにサポートしていません。

関数型プログラミング構造を言語に追加するコンパイラ/言語拡張機能はありますか?GCCは、ネストされた関数を言語拡張として提供します。ネストされた関数は親スタックフレームから変数にアクセスできますが、これは成熟したクロージャからはまだ遠いです。

たとえば、Cで本当に役立つと思うことの1つは、関数ポインターが期待される場所であればどこでも、ラムダ式を渡して、関数ポインターに減衰するクロージャーを作成できることです。C ++ 0xにはラムダ式が含まれます(これは素晴らしいと思います)。ただし、ストレートCに適用できるツールを探しています。

[編集]明確にするために、私は関数型プログラミングにより適したCの特定の問題を解決しようとはしていません。私がそうしたいのであれば、私はただそこにどんなツールがあるのか​​知りたいだけです。

4

13 に答える 13

95

GCCのネストされた関数を使用してラムダ式をシミュレートできます。実際、私にはそれを行うためのマクロがあります。

#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 に答える
42

FFCALLを使用すると、Cでクロージャを作成できます。--を呼び出すのと同じcallback = alloc_callback(&function, data)ような関数ポインタを返します。ただし、ガベージコレクションは手動で処理する必要があります。callback(arg1, ...)function(data, arg1, ...)

これに関連して、 AppleのGCCフォークにブロックが追加されました。これらは関数ポインターではありませんが、キャプチャされた変数のストレージを手動で構築して解放する必要をなくしながら、ラムダを渡すことができます(事実上、コピーと参照カウントが行われ、構文糖衣構文とランタイムライブラリの背後に隠されています)。

于 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は関数ポインタを使用して関数を渡すことができます。主な問題はクロージャがないことであり、型システムが邪魔になる傾向があります。M4などのCPPよりも強力なマクロシステムを探索できます。最終的に、私が提案しているのは、真のCは多大な努力なしにはタスクに対応できないということですが、Cを拡張してタスクに対応できるようにすることもできます。CPPを使用する場合、またはスペクトルの反対側に移動して他の言語からCコードを生成する場合、その拡張機能はCに最もよく似ています。

于 2008-10-19T05:24:49.710 に答える
5

クロージャーを実装したい場合は、アセンブリ言語とスタックのスワッピング/管理に慣れる必要があります。反対するのではなく、それがあなたがしなければならないことだと言っているだけです。

C で匿名関数をどのように処理するかはわかりません。ただし、フォン ノイマン マシンでは、asm で匿名関数を実行できます。

于 2008-10-21T14:02:31.693 に答える
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 などの既存の関数型言語の 1 つに相当するものになっているでしょう。

これらのセマンティクスを直接サポートする言語の構文を学習するだけのほうが、時間を有効に使うことができます。

于 2008-10-19T07:54:42.933 に答える
-3

Cについては知りません。Objective-Cには関数型の機能がいくつかありますが、OSXのGCCもいくつかの機能をサポートしていますが、関数型言語の使用を開始することをお勧めします.上記の機能がたくさんあります. 私は個人的にスキームから始めました。そうするのに役立つ The Little Schemer などの優れた本がいくつかあります。

于 2009-11-29T22:36:49.737 に答える