2

私はLisp が初めてで、 Paul GrahamによるANSI Common Lispを調べています。演習の 1 つは、apply のような関数を定義して、返される前に出力された数値がデフォルトで 8 進数で出力されるようにすることです。

私は次のことを試しました:

(let ((*print-base* 8))
  (defun like-apply (&rest args)
    (apply #'apply args)))

しかし、期待どおりに機能しませんでした:

(like-apply #'princ '(8)); returns 8 8 (expecting 10 8)

ただし、以下は機能します。

(defun apply8 (&rest args)
  (let ((*print-base* 8))
    (apply #'apply args)))

正しく戻ります:

(apply8 #'princ '(8)); returns 10 8 (as expected)

私の質問は、なぜ 2 番目の例が機能するのに、最初の例が機能しないのですか? どちらも*print-base*変数を操作しているようです。

4

2 に答える 2

6

Common Lisp は let を使用して、レキシカル変数と「特別な」(動的) 変数の両方をバインドします。期待した動作は字句的であり、観察した動作は動的でした。プリンター変数はすべて特殊なので、 let はそれらの動的バインディングを作成します。

プリンター変数は、動的バインドが役立つ理由の例で使用されることがあります。たとえば、*print-base* をバインドすることで princ の動作を制御できるという事実は、動的バインドによって有効になります。そうしないと、princ が定義されたときにアクティブな *print-base* のバインドを参照することになります。

この動作は、多くの Common Lisp プログラマーが特殊変数の *earmuffs* 命名規則に固執している主な理由です。defvar と defparameter はどちらも特別な変数を作成することに注意してください。

于 2013-07-28T05:52:36.913 に答える
4

あなたが観察する動作は正しいです。

比較すると参考になります

(let ((*print-base* 8))
  (defun f1 ()
    *print-base*))

(defun f2 ()
  (let ((*print-base* 8))
    *print-base*))

が返されることを発見するために返さ(f1)れます。10(f2)8

これは、*print-base*が の定義にバインドされているためです。つまり、が定義されf1ている8間ではありますが、実行されている間ではありません。f1

于 2013-07-28T04:55:46.730 に答える