1. ホーム
  2. c

[解決済み] 関数ポインタ、クロージャ、ラムダ

2023-04-14 15:37:43

質問

私は今ちょうど関数ポインタについて学んでいるところですが、このテーマに関する K&R の章を読んでいたとき、最初に私を襲ったのは、「あれ、これってクロージャに似てる」でした。

では、なぜ C スタイルの関数ポインタはクロージャやラムダと根本的に違うのでしょうか。私が言える限り、それは、関数を匿名で定義する慣習とは対照的に、関数ポインタがまだ定義された (名前の付いた) 関数を指しているという事実と関係があります。

なぜ、関数に関数を渡すことは、渡されるのが普通の日常的な関数である最初のケースよりも、無名の2番目のケースの方が強力であると見なされるのでしょうか?

この2つを密接に比較することがどのように、そしてなぜ間違っているのか、教えてください。

ありがとうございます。

どのように解決するのですか?

ラムダ(または クロージャ ) は、関数ポインタと変数の両方をカプセル化します。これが、C#で、できる理由です。

int lessThan = 100;
Func<int, bool> lessThanTest = delegate(int i) {
   return i < lessThan;
};

私はそこで匿名のデリゲートをクロージャとして使い(その構文はラムダと同等のものよりも少し明確でCに近い)、lessThan(スタック変数)をクロージャに取り込んでいます。クロージャが評価されるとき、lessThan (そのスタック・フレームは破壊されたかもしれない) は参照され続けるだろう。もし私が lessThan を変更したら、比較を変更する。

int lessThan = 100;
Func<int, bool> lessThanTest = delegate(int i) {
   return i < lessThan;
};

lessThanTest(99); // returns true
lessThan = 10;
lessThanTest(99); // returns false

C言語では、これは違法となります。

BOOL (*lessThanTest)(int);
int lessThan = 100;

lessThanTest = &LessThan;

BOOL LessThan(int i) {
   return i < lessThan; // compile error - lessThan is not in scope
}

引数を2つ取る関数ポインタを定義することができますが。

int lessThan = 100;
BOOL (*lessThanTest)(int, int);

lessThanTest = &LessThan;
lessThanTest(99, lessThan); // returns true
lessThan = 10;
lessThanTest(100, lessThan); // returns false

BOOL LessThan(int i, int lessThan) {
   return i < lessThan;
}

しかし、今私はそれを評価するときに2つの引数を渡さなければならない。lessThan がスコープにない別の関数にこの関数ポインタを渡したい場合は、チェーンの各関数に渡すか、グローバルに昇格させるかして、手動でそれを維持する必要があります。

クロージャをサポートするほとんどの主流の言語は無名関数を使用しますが、そのための要件はありません。無名関数なしでクロージャを持つことができ、クロージャなしで無名関数を持つことができます。

まとめ:クロージャとは、関数ポインタ+捕捉変数の組み合わせである。