4998


SchemeとCommon Lispの挙動を比べた時、クロージャを返す関数を利用するさいの違いについて、よくわからないところがあります。

例えば、Schemeの場合ですと、クロージャを作成する関数を定義し、そのクロージャを評価する場合、下のように書くことが出来ると思います:

[code]

    (define (closure-test x)
      (lambda () (print x)))
    
    ((closure-test "Hello, Closure."))
    ;; => print "Hello, Closure."
    
[/code]

そこで、同じようなことが出来ないかと思い、Common Lispでも下のように書いてみたところ、エラーが発生しました(処理系はClozure CLです):

[code]

    (defun closure-test (x)
      (lambda () (format t "~A~%" x)))
    
    ((closure-test "Hello, Closure.")) ;; Error
    (funcall (closure-test "Hello, Closure."))
    ;; => print "Hello, Closure"
    
[/code]

Schemeの場合はなんとなくわかるのですが、Common Lispについては、なぜこのような挙動になるのでしょうか?


CC BY-SA 3.0
2015-01-21T07:37:32.477
4987
2015-01-22T14:41:45.380
2015-01-21T08:08:40.427
939
939
question
common-lisp scheme
ラムダ式を利用したクロージャを利用するときの挙動について


関数呼び出しフォーム `(a b c d ...)` があった場合、Schemeでは`a`, `b`, `c`, `d`…
が一旦全て評価されてから、`a`の評価結果を関数として呼び出します。したがって`a`の位置に「関数を値とする式」を自由に書くことができます。

しかしCommon Lispでは、第一要素`a`は通常の意味で評価されません。これはCommon
Lispが、関数と変数の名前空間を分けるLisp-2であることに起因しています。  
例えば

[code]

    (let ((list '(1 2 3)))
      (list list list))
    
[/code]

これはCommon Lispでは((1 2 3) (1 2 3))となりますね。`(list list list)`
の最初の`list`は関数名とみなされ、後の二つの`list`のみがローカル変数参照となります。

もし第一要素も普通の式として評価するというルールにしてしまうと、ここで最初の`list`がローカル変数として評価されないことの説明がつかなくなってしまいます。

そこで、Common Lispでは第一要素を「関数名か、リテラルのラムダ式」に限定しているのです。

(ラムダ式が許されているのは歴史的経緯です。一貫性を求めるなら、`(funcall #'(lambda ...) arg ...)`
で統一した方が綺麗だったでしょう。)


CC BY-SA 3.0
2015-01-21T10:52:29.450
4998
2015-01-21T10:52:29.450
5258
4987
answer


CLには、まずシンボルというものがあります。  
CLのシンボルはc言語で言う構造体です。  
あえてC言語風に書くとするのなら以下のような感じになります。

[code]

    struct Symbol {
        function FUNCTION;
        macrolet MACROLET;
        string NAME;
        package PACKAGE;
        list PLIST;
        value VALUE;
    };
    
[/code]

ここで重要はメンバはfunctionとvalueで、functionには(基本的に)関数が入ることが期待され、valueには(基本的に)値が入ることが期待されます。

[code]

    (defun hoge (x) x)
    
[/code]

というコードを評価する場合、hoge.functionに、(lambda (x) x)が代入されます。

[code]

    (let ((x 1)) ....)
    
[/code]

というコードを評価する場合 x.valueに、1が代入されます。

[code]

    (hoge x y z)
    
[/code]

の場合は(hoge.function x.value y.value z.value)という風に評価されます。

以上を踏まえた上で、

[code]

    ((closure-test "Hello, Closure.")) 
    
[/code]

が何故ERRORなのかを考えていきます。  
これは

[code]

    (let ((f (closure-test "Hello, Closure."))) (f))
    
[/code]

と書き直すことが出来ます。  
このコードを順番に追っていくと、第一にf.valueに(closure-test "Hello,
Closure.")を評価したものが代入されます。第二に(f.function)が評価されます。しかし、f.functionには何も代入されていないため、エラーが起こります。


CC BY-SA 3.0
2015-01-22T14:41:45.380
5097
2015-01-22T14:41:45.380
5970
4987
answer