web-dev-qa-db-ja.com

Cの関数型プログラミングにはどのようなツールがありますか?

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

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

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

[編集]明確にするために、関数型プログラミングにより適したCの特定の問題を解決しようとはしていません。そうしたいのなら、どんなツールが出回っているのか興味があります。

141
Adam Rosenfield

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

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

38
ephemient

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; });
86
Joe D

関数型プログラミングはラムダに関するものではなく、純粋な関数に関するものです。したがって、以下は機能的なスタイルを広く促進します。

  1. 関数の引数のみを使用し、グローバル状態は使用しないでください。

  2. 副作用、つまりprintf、またはIOを最小限に抑えます。 IOを記述するデータを返します。これは、すべての関数で直接副作用を引き起こす代わりに実行できます。

これは単純なcで達成でき、魔法は必要ありません。

55
Andy Till

Hartel&Mullerの本、Functional C、今日(2012-01-02)は次の場所にあります: http://eprints.eemcs .utwente.nl/1077 / (PDFバージョン)へのリンクがあります。

16
FooF

頭に浮かぶ主なことは、コードジェネレーターの使用です。関数型プログラミングを提供する別の言語でプログラミングし、それからCコードを生成しますか?

それが魅力的な選択肢でない場合は、CPPを悪用して、そこへの道の一部を得ることができます。マクロシステムを使用すると、機能的なプログラミングのアイデアをエミュレートできます。 gccはこのように実装されていると聞いたことがありますが、チェックしたことはありません。

Cはもちろん関数ポインタを使用して関数を渡すことができます。主な問題はクロージャの欠如であり、型システムが邪魔になりがちです。 M4などのCPPよりも強力なマクロシステムを探索できます。最終的に、私が提案しているのは、真のCは多大な労力なしではタスクに任せないが、Cを拡張してタスクに任せることができるということです。 CPPを使用する場合、またはスペクトルのもう一方の端に移動して他の言語からCコードを生成できる場合、その拡張機能はCに最もよく似ています。

7
Jason Dagit

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

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

5
Paul Nathan

関数型プログラミングスタイルの前提条件は、ファーストクラスの関数です。次に許容する場合、ポータブル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 });

本質的に、関数/引数のペアとマクロの束として表されるクロージャを持つファーストクラスの関数を模倣します。完全なコードが見つかりました こちら

4
Viktor Shepel
3
ja.

かなり多くのプログラミング言語がCで書かれています。それらの一部は、ファーストクラスの市民としての機能をサポートしています。 gclo2などの「クロージャ」の場合 http://library.gnome.org/devel/gobject/unstable/chapter-signal.html#closure これは少なくとも関数型プログラミングに近づきました。そのため、これらの実装のいくつかを使用して関数型プログラミングを行うことはオプションかもしれません。

さて、またはOcaml、Haskell、Mozart/Ozなどを学ぶことができます;-)

よろしく

1
Friedrich

Felix言語 はC++にコンパイルされます。 C++を気にしないのであれば、それは踏み台になるかもしれません。

1

Cで関数型プログラミングを行う方法は、Cで関数型言語インタープリターを作成することでした。Fexlという名前を付けました。これは「関数式言語」の略です。

インタープリターは非常に小さく、-O3を有効にしたシステムで68Kまでコンパイルします。それはおもちゃでもありません-私は私のビジネスのために書くすべての新しい生産コードのためにそれを使用しています(投資パートナーシップのためのウェブベースの会計)。

(1)システムルーチン(fork、exec、setrlimitなど)を呼び出す組み込み関数を追加するか、(2)Fexlで記述できない関数を最適化する(例:検索部分文字列の場合)。

モジュールのメカニズムは、「コンテキスト」の概念に基づいています。コンテキストは、シンボルをその定義にマップする関数(Fexlで記述)です。 Fexlファイルを読むとき、好きなコンテキストで解決できます。これにより、カスタム環境を作成したり、制限された「サンドボックス」でコードを実行したりできます。

http://fexl.com

0