2015-12-07 20 views
6

(私はCは、機能的な方法で使用されるものではないことを理解ししかし、関数型プログラミングを学んだ、私はトラブル違った考えを持っている。。)Cで余分なパラメータを取る関数を持ち上げるには?

それらの制限を考える:ので

  • ありませんネストされた関数を打ち鳴らすは(ありませんので、ラムダ式)

は、我々はそのような目として、プロトタイプに合わせて追加のパラメータを取る関数fを持ち上げる方法を考えることができていないグローバル変数を禁じfが実行されると、新しいパラメータは無視されますか?ここで

は、私が何をしたいのかを詳細に示します。

私はそのタイプである関数f収まるようにしたい:引数としての機能を取る関数gで

f :: void f(char *s) 

(それを呼び出すに引数)、そのタイプである:

arg :: void f(unsigned int i, char *s) 

従って、Gの種類がある:

g :: void g(void (*f) (unsigned int, char)) 

Haskellではソリューションは、次のようになります。

g (const f) 

は多分マクロ魔法のいくつかの種類と、そのさえ可能ですか?

編集:理解を深めるため、実際のコードを示します。 ft_striterの本体を完成させる必要があります。目的は、インデックスiを使用するか使用しないかによって、関数fを文字列のすべての文字に適用することです。

void  ft_striter(char *s, void (*f) (char *s)) 
{ 
    ft_striteri(?); 
} 

static void ft_striteri_helper(char *s, unsigned int i, void (*f)(unsigned int, char*)) 
{ 
    if (*s) 
    { 
     f(i, s); 
     ft_striteri_helper(s + 1, i + 1, f); 
    } 
} 

void  ft_striteri(char *s, void (*f)(unsigned int, char*)) 
{ 
    ft_striteri_helper(s, 0, f); 
} 
+0

'f'は固定か変数か – fuz

+0

その質問に答える1つの方法は、関数本体でfを呼び出すことができるように、fをargに見えるようにする方法を見つけることです。 – qleguennec

+0

fはg – qleguennec

答えて

4

これは標準のポータブルCでは実装できません。問題は関数gです。間違って設計されている。これは、タイプ

void g(void (*f) (void *, unsigned int, char), void *ctxt) 

を有するべきであり、それが作るどのf呼び出しの最初の引数としてそのctxt引数を渡す必要があります。

今、あなたはあなたが

struct const_ctxt { 
    void (*fun)(char *); 
} 

void const(void *ctxt, unsigned int i, char *s) 
{ 
    ((struct const_ctxt *)ctxt)->fun(s); 
} 

void call_g_using_const_f(void (*f)(char *)) 
{ 
    struct const_ctxt *ctxt = malloc(sizeof (struct const_ctxt)); 
    ctxt->fun = f; 
    g(const, (void *)ctxt); 
    free(ctxt); 
} 

警告のようなコードで欲しいものを実装することができます:gへの引数はgの動的な範囲を逃れる可能性があるなら、あなたはctxtの割り当てを管理するための別の戦略を見つける必要がありますs。

gのパターンは、コードポインタとデータ "コンテキスト"/"環境"ポインタのペアをとることで、通常、より高レベルの言語がクロージャを実装する方法です。

+0

これは最も近い答えかもしれませんが、不幸にも、試作品が勇気があり、正確に述べられているものでなければならないため、これは機能しません。私はその答えを検証します。 – qleguennec

+0

@qleguennec Cでは、コールバックを含むよく設計されたライブラリは、この種のイディオムを使用して、機能的なクロージャに似たものを得ることがよくあります。閉包は、完全な抽象化ラムダ項(自由変数なし)を環境と組み合わせて(最初の 'k'引数の値を提供する)実装できます。 Cでは、これは関数ポインタと環境/コンテキストへの 'void *'ポインタとしてレンダリングされます。ライブラリがこの機能を提供できない場合、移植可能な回避策はありません。しかし、標準ライブラリでさえ、この( 'qsort')で失敗します: -/ – chi

+0

これはおそらくあなたを怒らせ、私はそれについて残念です。ドキュメントを読んだ後、私はその解決策を理解しました。これが私の問題に対する正しい答えです。しかし、私は自分の投稿にそれを述べなかったことを認識しています。構造体もtypedefもcファイルに宣言することはできません。私はヘッダーに必要な構造を宣言することができましたが、その後、すべての名前を見つける必要があります。とにかく、このソリューションを使用することはできませんが、とにかく感謝しました。 – qleguennec

2

あなただけの余分なパラメータを受け入れ、無視したいので、あなたが必要な署名でラッパー関数を作成することによって、これを達成することができ、元の関数に単純に委譲している。付

void f2(unsigned int i, char *s) { 
    f(s); 
} 

スコープでその機能を適切に宣言、あなたは単にあなたが望むなら、それはないよう

g(f2); 

機能f2()は、staticとして宣言することができます呼び出すことができますそれが定義されているファイルの外側のコードに表示されます。それは入れ子にする必要はありません。

+0

fはあなたが書いた関数には見えません – qleguennec

+0

@qleguennec、どうしてですか?確かに 'f()'が 'f2()'に見えるかどうかは 'f2()'自体に直接依存しません。 –

+0

@qleguennecそれは意味がありません。たぶんあなたの質問を編集し、問題を示すためにスケルトンCプログラムを提供する必要があります。 – user694733

-1

よく考えてみましょう。

最初にCにはラムダ式はありませんが、最新のC++標準ではありません。これが大きな問題であれば、Cの代わりにC++を検討する価値があります。

Cは、定義してfunction pointersを渡すことができます。そのため、で、関数を別の関数のパラメータとして渡すことは完全に正当です。

また、functions to take a variable number of argumentsを定義することもできます。これは便利な場合もあります。

また、Cマクロを使用すると便利です。あなたは、例えばこれを行うことができます:あなたもmacro overloadingを可能にするためのマクロで巧妙なことを行うことができます

#define f1(x) g((x), 1) 
#define f2(x) g((x), 2) 

int g(int x, int y) 
{ 
    return (x + y) ; 
} 

。これは、コーディングスタイルに関しては理想的ではありませんが、可能です。

だから、あなたが望むものを得るためのツールがいくつかあるかもしれません。

更新

私は私を作っていた間、私はここに彼の目標は、ラフの提案であることを理解ので、もしザ・OPは、彼のポストへの更新を追加しました。

OPが表示されているように、OPは選択された関数が他の場所で設定できるインデックス変数にアクセスすることを望んでいます。おそらくのようなもの:もちろん

typedef struct mystring_s { 
    char *s ; 
    int i ; 
    } mystring_t ; 

void f1(mystring_t *strp) 
{ 
    /* can access strp->s and strp->i */ 
} 

/* and similar for other functions f2, f2, etc. */ 

/* And to use this you can call any function using 
*/ 

void g(void (*fn)(mystring_t *), mystring_t *strp) 
{ 
    (*fn)(strp) ; 
} 

void set_i(mystring_t *strp, int v) 
{ 
    strp->i = v ; 
} 

/* for example */ 

mystring_t s ; 

/* set up s */ 

set_i(&s, 11) ; 
g(&f1, &s) ; 
set_i(&s, 31) ; 
g(&f2, &s) ; 

彼はまた、(彼は明らかにしたいか、可能ではないと思っていません)グローバルな値を使用して試みることができる、と彼はさらに、データと関数ポインタを格納することができます。

これは、実際にOPのための実行可能なアイデアであることを確かめるのは難しいです.Cが設計していないことを実際にやろうとしているからです。私は問題は彼が彼の頭の中にデザインアイデアを持っていて、それがCのために悪い選択だと思う。この要求がどのようになったかを見て、その全体的なデザインを、ハスケルの能力を置き換えるためにCに何かをさせる。

+0

関連する関数は* varargs関数ではないので、Varargs関数は前述の問題とは無関係です。 varargsなどを介した折り返しの量は、 'void(*)(unsigned int、char *)'型の関数ポインタがポインティングする関数が呼び出すべき*他の*関数に関する情報を運ぶことを許さない。それは関数の実装によって何らかの形で決定されなければなりません。 –

+0

マクロはOPの場合には有効ではありません。なぜなら、静的なものではなく*動的な解決策が必要なからです。 –

+0

おそらくC++とlambdaが代わりになるかもしれませんが、OPはそうではないと主張しています。その点で彼に矛盾する何らかの理由がない限り、ラムダ(またはC++)を示唆することは役に立たない。 –

関連する問題