Common Lisp高次関数学習ノート:function,funcall,apply...


Common Lisp高次関数学習ノート:function,funcall,applyの使い方
目次
  • 0概要
  • 関数functionの使い方
  • 2関数funcallの使い方
  • 3関数applyの使い方
  • 0概要
    高次関数はLispの大きな特色であり、高次関数とは一つの関数を別の関数のパラメータとして用いるものであり、普通の関数呼び出しを二次元平面上の活動と見なせば、高次関数は一つの次元を増やすことに相当する:高次関数を三次元立体空間での活動と見なすことができる.
    一般に、プログラミング言語には、<実用Common Lispプログラミング>で言うように、C言語では関数ポインタ、Perlではサブルーチンリファレンス、PythonではLispと同様に、C#ではエージェント、Javaでは反射と匿名クラスが使用されるため、開発者により柔軟で高度な構造抽象能力を提供するメカニズムが必要である.
    高次関数の1つの適用シーンは、Lispの関数sortなどの汎用ソートであり、呼び出し形式は以下の通りである.
    (sort '(6 2 5 3 7 0) #'>)
    

    異なる比較関数(すなわち#'の後の>)を選択することができ、このような実現は比較的柔軟であり、比較関数を交換したい場合、例えば<、sortのコードを修正する必要はなく、<関数を新しく書いてsortのパラメータとして伝達すればよい.
    また、コールバック関数やフックは、後で実行するためにコード参照を保存することもできる.
    1関数functionの使い方
    Lispでは、関数はオブジェクトであるため、関数functionによって関数オブジェクトを取得できます.
    CL-USER> (defun     (x) (* 10 x))
       
    CL-USER> (function    )
    #<Compiled-function     #x302000DEF23F>
    CL-USER> (defun foo (x) (* 2 x))
    FOO
    CL-USER> (function foo)
    #<Compiled-function FOO #x302000E2FC7F>
    CL-USER> 
    

    ここで形が「<...」のような形式は関数オブジェクトの構文である.
    関数functionには、次のように簡単な形式#'(井戸番号の単一引用符)があります.
    CL-USER> #'foo
    #<Compiled-function FOO #x302000CCF46F>
    CL-USER> #'   
    #<Compiled-function     #x302000CC64AF>
    CL-USER> 
    

    2関数funcallの使い方
    1つの関数オブジェクトが得られる以上、Lispは2つの関数funcallとapplyを提供して1つの関数オブジェクトを呼び出すことができ、それらの違いはパラメータにある.
    開発者が関数オブジェクトに渡すパラメータを明確に知っている場合、funcallを使用します.構文は次のとおりです.
    (funcall        1   2   3 …   n)
    

    すなわち、funcallの最初のパラメータは、関数オブジェクトである、2番目から最後のパラメータまで、関数オブジェクトのパラメータである.
    実際のコードは次のとおりです.
    CL-USER> (funcall #'foo 3)
    6
    CL-USER>
    

    ではfuncallで関数オブジェクトを直接使用できるかどうか試してみましょう.
    CL-USER> (funcall #<Compiled-function FOO #x302000CCF46F> 3)
    6
    CL-USER> (funcall #'    3)
    30
    CL-USER> (funcall #<Compiled-function     #x302000CC64AF> 34)
    340
    

    実験の結果、関数の対象としてfuncallで直接使用できることが明らかになった.
    このようにfuncallで関数を使用するのは、直接呼び出す関数とあまり変わらないようですが、ある関数をパラメータとして別のコードに渡し、このコードの内部でfuncalで関数を呼び出すのが一般的です.たとえば、次のようにします.
    CL-USER> (defun    (  1           )
                (loop for i from     to     by    do
                    (loop repeat (funcall   1 i) do (format t "*"))
                    (format t "~%")))
      
    CL-USER> (   #'exp 0 4 1/4)
    *
    *
    *
    **  
    **
    ***
    ****
    *****
    *******
    *********
    ************
    ***************
    ********************
    *************************
    *********************************
    ******************************************
    ******************************************************
    NIL
    CL-USER> 
    
           (   #'exp 0 4 1/4) ,                 .
    

    つまり、関数オブジェクトを直接渡すこともできます.
    CL-USER> #'exp
    #<Compiled-function EXP #x300000098BAF>
    CL-USER> (   #<Compiled-function EXP #x300000098BAF> 0 4 1/4)
    *
    *
    *
    **
    **
    ***
    ****
    *****
    *******
    *********
    ************
    ***************
    ********************
    *************************
    *********************************
    ******************************************
    ******************************************************
    NIL
    CL-USER> 
    

    このコードの関数「プロット」は、指数関数のオブジェクトexpを実パラメータとして受け入れる、この指数関数を用いて「最小値」と「最大値」の間にアスタリスクでASCII式の柱状図を「ステップ長」で描いた.
    3関数applyの使い方
    次に関数applyです.applyは開発者が関数オブジェクトのパラメータをリストに収集することを許可します.applyの構文は以下の通りです.
    (apply      (  1   2   3 …   n))
    

    次のように書くこともできます.
    (apply        1 (  2   3 …   n))
    

    または
    (apply        1   2 (  3 …   n))
    

    すなわち、その最初のパラメータは関数オブジェクトである、最後のパラメータはリストである、関数オブジェクトとリストとの間に複数の個別のパラメータがある.
    上の「グラフ」関数を例にとると、4つのパラメータがあります.1つ目は関数オブジェクトで、後ろの3つは関数オブジェクトのパラメータです.applyを使用すると、リストの形式に書くことができます.
    (          )
    

    たとえば、次のように呼び出すことができます.
    CL-USER> (funcall #'   #'exp 0 4 1/4)
    *
    *
    *
    **
    **
    ***
    ****
    *****
    *******
    *********
    ************
    ***************
    ********************
    *************************
    *********************************
    ******************************************
    ******************************************************
    NIL
    
    CL-USER> (defvar *    * '(0 4 1/4))
    *    *
    CL-USER> (apply #'   #'exp *    *)
    *
    *
    *
    **
    **
    ***
    ****
    *****
    *******
    *********
    ************
    ***************
    ********************
    *************************
    *********************************
    ******************************************
    ******************************************************
    NIL
    CL-USER> 
    

    もちろん、この文を試してみてください.
    CL-USER> (defvar *       * '(exp 0 4 1/4))
    *       *
    CL-USER> 
    

    実践は発見して、このパラメータは受け入れられなくて、つまりapplyのリストのパラメータの中で関数のオブジェクトをリストの第1の位置に置くことができなくて、その他の位置は実行可能かどうか、私は試したことがなくて、興味のある友达は自分で試してみることができます.
    最后に补充します:中は関数の说明に対して更に详しくて深くて、初心者が真剣に読むことを提案します–私も読んでいます.:)