13

Paul Graham の ANSI Common Lisp page 110 の例を説明できる人はいますか?

この例では、 &rest と lambda を使用して関数型プログラミング機能を作成する方法を説明しようとしています。それらの 1 つは、関数の引数を構成する関数です。それがどのように機能したかを説明するものは何も見つかりません。コードは次のとおりです。

(defun compose (&rest fns)
  (destructuring-bind (fn1 . rest) (reverse fns)
    #'(lambda (&rest args)
        (reduce #'(lambda (v f) (funcall f v))
                rest
                :initial-value (apply fn1 args)))))

使用法は次のとおりです。

(mapcar (compose #'list #'round #'sqrt)
        '(4 9 16 25))

出力は次のとおりです。

((2) (3) (4) (5))

2 行目と 6 行目は特に魔法のように見えます。

4

3 に答える 3

10

この関数は、最後の関数から最初の関数まで各関数を呼び出すクロージャcomposeを返し、各関数呼び出しの結果を次の関数に渡します。

を呼び出した結果のクロージャは、(compose #'list #'round #'sqrt)最初にその引数の平方根を計算し、結果を最も近い整数に丸め、結果のリストを作成します。引数として 3 を指定してクロージャーを呼び出すことは、 を評価することと同じ(list (round (sqrt 3)))です。

destructuring-bindは、式を評価しての(reverse fns)引数をcompose逆の順序で取得し、結果リストの最初の項目をfn1ローカル変数にバインドし、結果リストの残りを残りのローカル変数にバインドします。したがって、 fn1はfnsの最後の項目を保持します#'sqrt

reducefnsは、累積された結果を使用して各関数を呼び出します。は関数:initial-value (apply fn1 args)に初期値を提供し、reduce複数の引数を指定してクロージャーを呼び出すことをサポートします。複数の引数を必要としない場合は、次のcomposeように簡略化できます。

(defun compose (&rest fns)
  #'(lambda (arg)
      (reduce #'(lambda (v f) (funcall f v))
              (reverse fns)
              :initial-value arg)))
于 2011-05-08T15:07:27.180 に答える
5

さて、ここに行きます:

  1. 与えられた関数を取り、それを逆にして(あなたの例では、 になります(#'sqrt #'round #'list))、最初のアイテムを に貼り付け、fn1残りを に貼り付けrestます。fn1= #'sqrt、およびrest=があります(#'round #'list)
  2. (apply sqrt args)次に、 (結果のラムダに与えられる値はどこargsにある) を初期値として使用し、各反復で呼び出し元の次の関数を取得して、折り畳みを実行restします。
    1. 最初の反復で(round (apply sqrt args))は になり、2 回目の反復では になります(list (round (apply sqrt args)))
  3. sqrt興味深いことに、複数の引数を取ることができるのは初期関数 (あなたの場合) だけです。残りの関数は、チェーン内の特定の関数が複数の値を返す場合でも、単一の引数のみで呼び出されます。
于 2011-05-08T15:13:52.443 に答える