[セットトップ]関数閉包についても述べる

3617 ワード

動的言語(python,javascript,Lua,perlなど)では、閉パッケージの概念をよく耳にします.閉パッケージ(closure)は関数式プログラミングの重要な構文構造です.関数式プログラミングはプログラミングモデルである(プロセス向けプログラミングもオブジェクト向けプログラミングもプログラミングモデルである).その解釈や例については、ネット上でたくさん探しています.しかし、簡潔で実用的な解釈は多くない.
実際、C++またはCコードを書いたプログラマであれば、以下の2つの問題を理解する必要があります.
(1)閉包とは何ですか.
(2)閉パッケージは開発者にどのような利便性をもたらすのか.
まず,閉パケットとは何かを説明し,まず概念を与える:閉パケットは関数とコンテキスト環境の組合せ体である.
英語の本が好きな子供靴はこの定義を参考にすることができます:In computer science,a closure is a function that is evaluated in an environment containing one ormore bound variables.Whencalled, the function can access these variables.
この概念をどのように理解しますか?閉パッケージは、通常の関数の内部で定義され、コンテキスト環境が記憶されている特殊な「関数」とも考えられます.where a function remembers what happens around it
どうして特別なの?まず、通常の関数の内部に作用します.次に、通常の関数では、呼び出されるときにパラメータがユーザーに渡される必要があります.閉じたパケットはそうではありません.そうしないと、周囲のパラメータを覚えることができます.例えば(Lua閉パッケージ):
function newCounter ()
      local i = 0
      return function ()   --   
               i = i + 1
               return i
             end
end
    
    c1 = newCounter()
    print(c1())  --> 1
    print(c1())  --> 2

閉パケットはnewCounterの範囲内の局所変数iを記憶する.
次に、C、C++言語でよく知られているcallbackおよび関数オブジェクトを通じて、閉パッケージをより比較的に認識しましょう.
C言語では、コールバック関数を使用する場合、通常は先に定義されます.コールバック関数の定義は、通常、関数ポインタと関連パラメータから構成されます.通常、コールバックを呼び出す前に、コールバック関数に追加のパラメータを渡してデータを格納する必要があります.このパラメータをコールバック関数のコンテキストと呼び,変数名はctx(contextの略)を用いる.このコンテキストに何を保存しますか?それは具体的なコールバック関数によって決まり、任意のデータ型を保存するためにvoid*を選択してこのコンテキストを表します.
 
たとえば、チェーンテーブルのデータの合計を計算するコールバックを定義します.
定義関数ポインタタイプ:typedef DlistRet(*CallbackFunc)(void*ctx,void*data);ループ中にコールバックを呼び出します.
DListRet dlist_foreach(DList* thiz, DListVisitFunc visit, void* ctx) { DListRet ret = DLIST_RET_OK; DListNode* iter = thiz->first; while(iter != NULL && ret != DLIST_RET_STOP) { ret = visit(ctx, iter->data); iter = iter->next; } return ret; }
ユーザは和を求めるコールバック関数を実現する:
和を求めるコールバック関数の実装:static DlistRet sum_cb(void*ctx,void*data){long long*result=ctx;//前回の計算結果をコンテキストに格納*result+=(int)data;return DLIST_RET_OK;}以上のコード例では、コールバックにctxを渡すことで、合計を計算する関数が正しく実行されることが分かる.
閉パケットはコールバック関数に似ており、関数とコンテキストから構成されていることがわかります.異なる点は、コールバック関数が事前に宣言され定義される必要があることです.また,ユーザがコールバックを使用する場合,パラメータの変換を行い,コールバックに正しいコンテキストを伝達する必要がある.
C++の関数オブジェクトを知っている場合は、閉パッケージも関数オブジェクトと少し似ています.関数オブジェクトは()オペレータを再ロードします.これも、閉パッケージが「匿名関数」と呼ばれる場合がある理由です.
では、閉鎖は開発者にどのような利便性をもたらすのでしょうか.なぜ閉鎖する必要があるのですか?答えは,閉パケット自体がコンテキスト環境を含むため,ユーザは関数オブジェクトやcallbackのように事前に定義する必要がなく,パラメータの伝達や変換を行う必要もないことが明らかである.
さらに、閉包は、関数に必要な定義パラメータの数を効果的に減少させる.これは並列演算にとって重要な意義がある.並列演算環境では、各コンピュータに関数を担当させ、1台のコンピュータの出力と次のコンピュータの入力を直列に接続することができます.最終的に,我々は流水線のように動作し,直列のコンピュータクラスタの一端からデータを入力し,他端からデータを出力する.このようなシナリオは,パラメータ入力が1つしかない関数に最適である.閉鎖すればこの目的を実現することができる.
並列演算はホットスポットと呼ばれています.これも関数式プログラミングが熱くなる重要な原因です.関数式プログラミングは1950年代にすでに存在していたが,応用は広くなかった.しかしながら,上述した流水線式の動作並列クラスタ過程は,関数式プログラミングに適している.関数プログラミングという天然の優位性のため、ますます多くの言語が関数プログラミングのモデルのサポートに参加し始めた.
次に、いくつかの例を挙げて説明します.
(1)Lua閉パッケージ例:
GUIプログラミングをしていると仮定すると、ユーザーがデジタルボタンをクリックすると、ボタンのインタフェース表示がクリックに伴って変化するという効果が得られます.C言語では、この効果を実現するために、通常、ボタンのコールバックOnPressButton(Context User_Data)を定義し、ユーザーがクリックしたときにコールバックを呼び出し、異なるパラメータを入力します.クローズド・パッケージを使用すると、より簡潔で迅速に実行できます.次の例のコードを参照してください.
 
function digitButton (digit)       return Button{ label = digit,                      action = function ()                                 add_to_display(digit)                               end                    }     end
(2)javaScript閉パッケージ例:
次のように仮定します.
settimeoutは、settimeout(code,millisec)という関数の実行を遅延させることができます.最初のパラメータは実行する必要がある関数またはコードであり、2番目のパラメータは遅延のミリ秒数である.一般的な使い方: