6

それで、私はLisp
setq と defvar を読みました
特に setf と defvar の違いについて。そこで、このアイデアを少しいじってみることにしました。

CL-USER> (defun foo ()
       (setf x 10)
       (print x))

; in: DEFUN FOO
;     (SETF X 10)
; ==>
;   (SETQ X 10)
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
FOO
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {10040F1543}>.
CL-USER> (foo)

10 
10
CL-USER> x
10

さて、setf を使用して既存の変数の値を変更する必要があることはわかっていますが、未定義の変数の警告は SBCL でかなりうまく処理されているようです (ただし、異なる CL 実装ではこれを異なる方法で処理する可能性があることを読みましたが、そうではありません)。するのが最善です)。

2 番目のテストに入ります。

CL-USER> (defun bar ()
       (defvar y 15)
       (print y))

; in: DEFUN BAR
;     (PRINT Y)
; 
; caught WARNING:
;   undefined variable: Y
; 
; compilation unit finished
;   Undefined variable:
;     Y
;   caught 1 WARNING condition
BAR
CL-USER> y
; Evaluation aborted on #<UNBOUND-VARIABLE Y {10045033D3}>.
CL-USER> (bar)

15 
15
CL-USER> y
15

リンクに従って、 setf を defvar に変更しました。これにより、変数を一度に作成してバインドする必要があると思います。ここで、未定義の変数の警告が (print y) 行にプッシュされます...ここで何が起こっているのでしょうか?

二次的な質問として、関数内で割り当てられた変数の値は、Python の場合のように、関数の外ではアクセスできないと予想しています。

>>> def foo():
...     x = 10
...     print x
... 
>>> foo()
10
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

これは、一般的な Lisp がスコープを処理する方法、つまり defvar が「グローバルな特殊な変数」を作成する方法と関係があると推測しています...だから最後にもう一度試してみました (let ...)

CL-USER> (defun baz () (let ((z 10)) (print z)) (incf z 10) (print z))

; in: DEFUN BAZ
;     (INCF Z 10)
; --> LET* 
; ==>
;   (SETQ Z #:NEW0)
; 
; caught WARNING:
;   undefined variable: Z
; 
; compilation unit finished
;   Undefined variable:
;     Z
;   caught 1 WARNING condition

そして、defvar、defparameter、setf、および setq の違いを読んだ後、これは正しく機能するようです:

CL-USER> (defun apple ()
       (defparameter x 10)
       (print 10))

APPLE
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {1004436993}>.
CL-USER> (apple)

10 
10
CL-USER> x
10

私の質問を繰り返します: 1) setf、defvar、および let で実際に何が起こっているのですか?
2) Python の例のように、一般的な Lisp で関数内の変数のスコープを取得する方法はありますか?

4

2 に答える 2

6

答え2) DEFVAR変数を定義します。しかし、それは実行されていません。そのため、コンパイラはフォーム内の変数を認識しません-printフォームをコンパイルするときDEFUN.. DEFUNしたがって、トップレベルではありません。トップレベルのフォームとして、コンパイラは を認識し、それがグローバルな特殊変数であるDEFVARことに気付くでしょう。y

私の質問を繰り返します: 1) setf、defvar、および let で実際に何が起こっているのですか? 2) Python の例のように、一般的な Lisp を関数内の変数のスコープにする方法はありますか?

1)SETF変数値を設定しますが、定義はしません。その変数が定義されていない場合、Common Lisp 標準は実際に何が起こるかを述べていません。ほとんどの Common Lisp 実装は、何か役に立つことをします。通常、変数が特別に宣言されているかのように実行されます (したがって、警告も表示されます)。

DEFVARグローバルな特殊変数を定義するために、トップレベルのフォーム (通常は関数内ではない) として使用されます。DEFVARは変数名が特別であると宣言しているので*y*、単にy.

LETローカル変数のスコープを定義します。

2) Common Lisp 関数には、変数を導入するためのパラメータ リストがあります。それ以外は、変数のスコープを定義していません。関数内にローカル変数を導入する場合は、 を使用しますLET

>>> def foo():
...     x = 10
...     print x

(defun foo ()
  (let ((x 10))
    (print x)))

繰り返しますが、関数は変数のスコープを提供しません。そのため、関数内で変数を割り当てると、関数ローカルであると自動的に定義されます。LET代わりに使用してください。

LETまた、それは構文的 sugarであることに注意して(let ((a 1) (b 2)) (+ a b))ください((lambda (a b) (+ a b)) 1 2)。これは単純な関数アプリケーションであり、人間の読者向けに改善するために別の方法で記述されています。

Common Lisp では、古い構文もサポートされています。

(defun foo (&aux (x 10))
  (print x))

X上記では、 a が行うように、ローカル変数 を定義してLETいます。

于 2013-01-14T09:38:44.800 に答える
4

短くシンプルにしておきます。

1)setf、defvar、letで実際に何が起こっているのですか?

  • defvarおよびdefparameterは、グローバルに特殊な変数を宣言および設定するために使用されます。それらは、名前がすでにバインドされている場合にのみ異なります。

  • setf割り当てに使用されます(特殊変数、字句変数、およびその他の(場合によってはカスタムの)設定可能な場所へ)。

  • let(およびlet*)は、本体内に表示される新しい変数バインディングを作成します。(グローバルまたはローカル)宣言に応じて、字句または特殊なバインディングを作成できます。特別な宣言がない場合は、字句バインディングが作成されます。

2)Pythonの例のように、関数内の変数をスコープする一般的なlispを取得する方法はありますか?

letバインディングが表示されているの本文内にコードを配置します。

CL-USER> (defun baz ()
           (let ((z 10))
             (print z)
             (incf z 10) ; i.e. (setf z (+ z 10))
             (print z)))
BAZ
CL-USER> (baz)

10 
20 
20
CL-USER> z
; Evaluation aborted on #<UNBOUND-VARIABLE #x186C611E>.
于 2013-01-14T09:43:05.380 に答える