19

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で行う方法を知りたいです。私は本当に高階関数のスタイルを維持したいのですが、それが嫌いなら私はこれを他の方法で行う方法を大いに感謝します。

4

8 に答える 8

21

技術的には、高階関数は関数を取得または返す関数にすぎません。したがって、qsortのようなものはすでに高階です。

関数型言語(高階関数が実際に役立つ場所)にあるラムダ関数のようなものを意味する場合、それらはかなり難しく、現在の標準Cでは自然に実行できません。これらは一部ではありません。言語。Appleのブロック拡張は最良の候補です。GCC(およびLLVMのCコンパイラ)でのみ機能しますが、非常に便利です。うまくいけば、そのようなものがうまくいくでしょう。関連するリソースは次のとおりです。

于 2010-03-29T03:52:14.160 に答える
8

Cで高階関数を実装する際の大きな問題は、重要なことを行うにはクロージャが必要なことです。クロージャは、アクセスできるローカル変数を含むデータ構造で拡張された関数ポインターです。クロージャの背後にある全体的な考え方は、ローカル変数をキャプチャし、それらを関数ポインタと一緒に渡すことであるため、コンパイラのサポートなしで行うのは困難です。また、コンパイラのサポートがあっても、変数がスコープ外に存在する可能性があり、変数をいつ解放するかを判断するのが難しいため、ガベージコレクションなしで実行するのは困難です。

于 2010-03-29T05:28:17.970 に答える
6

ストレート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)の各文字を間にスペースを入れて印刷し、すべての文字を合計して累積し、実行するたびに文字のリストを再度印刷します。

お役に立てれば。ちなみに、ブロックはMac OS X Snow Leopard APIで非常に目立ちます。また、今後のC ++ 0x標準に含まれていると思いますので、それほど珍しいことではありません。

于 2010-03-29T03:37:34.847 に答える
6

これは質問への答えです:ここにリダイレクトされる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)); 
}

それがどのように機能するかの例。cons(f、cons(g、cons(h、nil)))の短い表記として(fgh)を使用します。これは、指定された引数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やHaskellのような型付き言語でポリモーフィックリストタイプを使用した場合、compのタイプは次のようになります。

comp :: ([a -> a],a) -> a

そのコンテキストでは、リスト内のすべてのメンバーが同じタイプであるためです。この意味で、Cはより柔軟になります。多分何かのような

typedef void (*fun)();

また

typedef (*fun)();

Cマニュアルがこれについて何と言っているかを見る必要があります。また、隣接するすべての関数に互換性のあるタイプがあることを確認してください。

構成する関数は純粋である必要があります。つまり、副作用や自由変数がない必要があります。

于 2017-05-20T14:35:28.193 に答える
5

プレーンCでこれを行うことに熱心な場合は、ファンクター(高階関数)の呼び出し元から渡された関数にコンテキストポインターを渡すオプションを含めることを忘れないでください。これにより、十分にシミュレートできます。あなたが物事を十分に簡単に機能させることができるクロージャの。そのポインタが指しているのは...まあ、それはあなた次第ですが、ファンクターのAPI(または、GLibの世界やTcl C APIvoid*などの多くのエイリアスの1つ)にある必要があります。gpointerClientData

[編集]:あなたの例を使用/適応するには:

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...
}

これはそれを行う方法の簡単な例ですが、それはあなたに道を示すはずです。

于 2010-03-29T14:37:10.280 に答える
3

実際には、興味深い高階関数アプリケーションにはクロージャが必要です。これは、Cでは、構造体関数の引数を手動で定義して入力するという、面倒でエラーが発生しやすいルーチンを伴います。

于 2010-03-29T03:50:27.123 に答える
2

ストレートCで行うのは非常に困難です。C++ではより可能です(ファンクターのチュートリアルまたはBoostのバインドおよび関数ライブラリを参照してください)。最後に、C ++ 0xはラムダ関数のネイティブサポートを追加します。これにより、関数が依存するすべての変数をクロージャでキャプチャすることができます。

于 2010-03-29T04:35:02.390 に答える
1

高階関数を作成する場合は、Cを使用しないでください。問題にはCの解決策があります。それらはエレガントではないかもしれませんし、あなたが理解しているよりエレガントかもしれません。

[編集]これを実現する唯一の方法は、スクリプト言語を使用することだと提案しました。他の人は私にそれを呼びかけました。だから、私はその提案をこれに置き換えています:[/ Edit]

何を達成しようとしていますか?クロージャを模倣したい場合は、クロージャをサポートする言語を使用してください(ライブラリを介して、Ruby、lua、javascriptなどに結び付けることができます)。コールバックを使用したい場合は、関数ポインタで問題ありません。関数ポインタはCの最も危険な領域(ポインタと弱い型システム)を組み合わせているので注意してください。関数ポインタ宣言も読むのが面白くありません。

関数ポインタを使用しているCライブラリがいくつかあります。ライブラリを作成している場合は、それらも使用する必要があるかもしれません。自分のコード内でそれらを使用しているだけの場合は、おそらくCでは考えていません。lisp、scheme、ruby、または...で考えており、Cで書き込もうとしています。Cの方法を学びましょう。

于 2010-03-29T03:35:24.670 に答える