7

問題の性別は

(((lambda (b)
  (lambda (a)
    (+ b a))) 3) 5)

私には、これは に評価されるように見えますが8、他の Lisp (Racket など) では評価されますが、elisp では代わりに次のエラーがスローされます。

Debugger entered--Lisp error: (invalid-function ((lambda (b) (lambda (a) (+ b a))) 3))

と伝えているようです

((lambda (b)
  (lambda (a)
    (+ b a))) 3)

有効な関数ではありません。これは間違っているようです。その式を評価すると、

(lambda (a) (+ b a))

これは私にとって有効な機能のように見えます。なぜこれが起こるのか誰にも分かりますか?動的スコープと関係がありますか?

4

4 に答える 4

14

ここには 2 つの問題があります。他の回答が指摘しているように、最初は構文の問題です。質問で言及されている2番目は、スコープの問題です。

構文の問題

Emacs Lisp (およびLisp-2(f args...)ファミリーの他の Lisp) では、関数呼び出しはwherefが関数値を持つシンボルまたはlambdaのように見えます。例えば、

(list 1 2 3)  
=> (1 2 3)

list関数バインディングがあるためです。また、

((lambda (x y) (list x x y y)) 1 2)
=> (1 1 2 2)

表現だから(lambda (x y) (list x x y y))lambdaただし、値が関数である何かを使用することはできません。

(let ((id (lambda (x) x)))
  (id 3))

信号 a Lisp error: (void-function id)。しかし、次を使用して関数値を呼び出すことができますfuncall

(let ((id (lambda (x) x)))
  (funcall id 3))
=> 3

注: これは妥当な見方ですが、実際にはもう少し複雑です。詳細および関数間接化などの難解なビットについては、マニュアルの9.2 フォームの種類を参照してください。

これで、構文の問題に対処できます。どの関数がどの引数を取得しているかを示すために少し再フォーマットされた元のコードは次のとおりです。

(((lambda (b)
    (lambda (a)
      (+ b a)))
  3)
 5)

私が理解しているように、その意図は、最初(lambda (b) ...)に引数3を指定して呼び出して、無名関数を取得すること(lambda (a) ...)です。Emacs Lisp では、次のようになります。

((lambda (b)
   (lambda (a)
     (+ b a)))
 3)
=> (lambda (a) (+ b a))

ここで、返された無名関数を で呼び出すことも必要です5。私たちはそれを行うために使用funcallします:

(funcall ((lambda (b)
            (lambda (a)
              (+ b a)))
          3)
         5)

スコーピングの問題

残念なことに、このコードはLisp error: (void-variable b). ここで、動的スコープと字句スコープの問題に最終的に遭遇します。変数bは動的にバインドされているため、その値は無名関数で保持されません(lambda (a) (+ b a))。フォーム全体をバインドするもので囲み、何が起こるかを確認することで、これが起こっていることを確認できますb

(let ((b 100))
  (funcall ((lambda (b)
              (lambda (a)
                (+ b a)))
            3)
           5))
=> 105

私はあまり Emacs Lisp ハッカーではないので、Emacs でレキシカル クロージャを取得する最善の方法がわかりません。Emacs 24 にはそれがあると読みましたが、ここではまだ 23 です。ただし、この回答に基づいて、lexical-let必要な結果を得るために使用できます。

(funcall ((lambda (b)
            (lexical-let ((b b))
              (lambda (a)
                (+ b a))))
          3)
         5)
=> 8

lexical-let必要な字句バインディングを確立して、無名関数(lambda (a) ...)3それを保持できるようにします。より具体的には、 の字句バインディングを導入しました。参照するのbはその字句バインディングです(lambda (a) …)。実際、返された無名関数を見ると、単純な(lambda (a) (+ b a))ではなく、より複雑な (そしてあまり役に立たない) 方法で出力されます。

((lambda (b)
   (lexical-let ((b b))
     (lambda (a)
       (+ b a))))
 3)
=> (lambda (&rest --cl-rest--) (apply (lambda (G27322 a) (+ ... a)) (quote --b--) --cl-rest--))

余談ですが、字句的にバインドされた変数bが動的にバインドされた変数と同じ名前であることは問題ではありませんb(lexical-let ((c b)) ... (+ c a) ...)代わりに を使用することもできました。

于 2013-06-14T12:13:36.117 に答える
3

短い答え: Lisp-1 と Lisp-2の効果です。

少し長い答え: アプリケーションの場合、Emacs Lisp はリストの最初のシンボルの関数値を呼び出しますがlambda、単純な関数オブジェクトだけを返します。つまりfuncall、物事を機能させるために使用する必要があります。 funcall2 番目の引数を残りの引数に適用します ( とよく似applyていますが、後者は最後の引数をリストであると想定しています)。

(funcall (lambda (a) (funcall (lambda (b) (+ a b)) 5)) 3)
=> 8

詳細については、emacs のドキュメント (例: C-h f funcall) を参照してください。

于 2013-06-14T08:15:43.633 に答える