57

Practical Common Lispがグローバル変数(defvar *db* nil)の設定に使用していることがわかります。同じ目的で使っていいんじゃないの?setq

と を使用する利点/欠点は何defvarですかsetq?

4

4 に答える 4

57

変数を導入する方法はいくつかあります。

DEFVARDEFPARAMETERは、グローバル動的変数を導入します。DEFVARすでに定義されていない限り、オプションで何らかの値に設定します。DEFPARAMETER常に指定された値に設定します。 SETQは変数を導入しません。

(defparameter *number-of-processes* 10)

(defvar *world* (make-world))     ; the world is made only once.

、、、、、などの名前のDEFVAR変数を使用したくない可能性が高いことに注意してください。これらの変数は特別に宣言され、元に戻すのが難しいためです。特別な宣言はグローバルであり、変数のそれ以降のすべての使用は動的バインディングを使用します。xystreamlimit

悪い:

(defvar x 10)     ; global special variable X, naming convention violated
(defvar y 20)     ; global special variable Y, naming convention violated

(defun foo ()
  (+ x y))        ; refers to special variables X and y

(defun bar (x y)  ; OOPS!! X and Y are special variables
                  ; even though they are parameters of a function!
  (+ (foo) x y))

(bar 5 7)         ; ->   24

ベター: 常に特別な変数を*その名前でマークしてください!

(defvar *x* 10)     ; global special variable *X*
(defvar *y* 20)     ; global special variable *Y*

(defun foo ()
  (+ *x* *y*))      ; refers to special variables X and y

(defun bar (x y)    ; Yep! X and Y are lexical variables
  (+ (foo) x y))

(bar 5 7)           ;  ->   42

ローカル変数は、DEFUNLAMBDALETMULTIPLE-VALUE-BINDなどで導入されています。

(defun foo (i-am-a-local-variable)
   (print i-am-a-local-variable))

(let ((i-am-also-a-local-variable 'hehe))
  (print i-am-also-a-local-variable))

現在、上記の 2 つの形式のローカル変数は、 SPECIALと宣言されていない限り、デフォルトでレキシカルです。次に、それらは動的変数になります。

次に、変数を新しい値に設定するためのフォームもいくつかあります。 SETSETQSETFなど。SETQレキシカル変数とSETF特別な (動的) 変数の両方を設定できます。

移植可能なコードでは、既に宣言されている変数を設定する必要があります。宣言されていない変数を設定した場合の正確な効果は、標準では定義されていません。

したがって、Common Lisp の実装が何を行うかを知っている場合は、次を使用できます。

(setq world (make-new-world))

トップレベルのRead-Eval-Print-Loopで。ただし、効果は移植性がないため、コードでは使用しないでください。通常SETQ、変数を設定します。しかし、一部の実装では、変数SPECIALを認識していないときに宣言することもあります (CMU Common Lisp はデフォルトでそれを行います)。それはほとんどの場合、人が望むものではありません。自分が何をしているのかわかっている場合はカジュアルな使用に使用しますが、コードには使用しないでください。

こっちも一緒:

(defun make-shiny-new-world ()
  (setq world (make-world 'shiny)))

まず、グローバルな特殊変数であることを明確にするために、そのような変数は*world* (周囲の*文字で) のように記述する必要があります。DEFVAR第二に、withまたはDEFPARAMETERbeforeで宣言されている必要があります。

典型的な Lisp コンパイラは、上記の変数が宣言されていないと文句を言うでしょう。Common Lisp にはグローバル レキシカル変数が存在しないため、コンパイラは動的ルックアップ用のコードを生成する必要があります。すると一部のコンパイラは、わかりました。これは動的ルックアップであると仮定します。それを特別なものとして宣言しましょう。

于 2010-10-04T16:45:23.587 に答える
22

defvarは動的変数を導入setqし、動的またはレキシカル変数に値を割り当てるために使用されます。動的変数の値は、関数を呼び出す環境で検索されますが、レキシカル変数の値は、関数が定義された環境で検索されます。次の例は、違いを明確にします。

;; dynamic variable sample
> (defvar *x* 100)
*X*
> (defun fx () *x*)
FX
> (fx)
100
> (let ((*x* 500)) (fx)) ;; gets the value of *x* from the dynamic scope.
500
> (fx) ;; *x* now refers to the global binding.
100

;; example of using a lexical variable
> (let ((y 200))
   (let ((fy (lambda () (format t "~a~%" y))))
     (funcall fy) ;; => 200
     (let ((y 500))
       (funcall fy) ;; => 200, the value of lexically bound y
       (setq y 500) ;; => y in the current environment is modified
       (funcall fy)) ;; => 200, the value of lexically bound y, which was 
                     ;; unaffected by setq
     (setq y 500) => ;; value of the original y is modified.
     (funcall fy))) ;; => 500, the new value of y in fy's defining environment.

動的変数は、デフォルト値を渡すのに役立ちます。たとえば、動的変数*out*を標準出力にバインドして、すべての io 関数のデフォルト出力にすることができます。この動作をオーバーライドするには、ローカル バインディングを導入するだけです。

> (defun my-print (s)
        (format *out* "~a~%" s))
MY-PRINT
> (my-print "hello")
hello
> (let ((*out* some-stream))
    (my-print " cruel ")) ;; goes to some-stream
> (my-print " world.")
world

レキシカル変数の一般的な使用法は、クロージャを定義して、オブジェクトを状態でエミュレートすることです。y最初の例では、バインディング環境の変数は、fy事実上、その関数のプライベート状態になりました。

defvarまだ割り当てられていない場合にのみ、変数に値を割り当てます。したがって、次の の再定義は*x*、元のバインディングを変更しません。

> (defvar *x* 400)
*X*
> *x*
100

*x*を使用して新しい値を割り当てることができますsetq

> (setq *x* 400)
400
> *x*
400
> (fx)
400
> (let ((*x* 500)) (fx)) ;; setq changed the binding of *x*, but 
                         ;; its dynamic property still remains.
500
> (fx)
400
于 2010-10-04T14:34:47.103 に答える
9

DEFVARは、新しい変数を確立します。 SETQは変数に割り当てます。

私が使用したほとんどの Lisp 実装は、まだ存在しない変数に対して SETQ を実行すると、警告を発します。

于 2010-10-04T14:03:03.690 に答える
8

defvarどちらもグローバル変数をdefparameter導入します。Ken が指摘setqするように、変数に代入します。

さらに、defvar以前にdefvar-ed されたものを上書きしません。Seibel は本書の後半 (第 6 章) で次のように述べています。

http://www.gigamonkeys.com/book/variables.html

たとえば*db*、単純なデータベースの章にデータベースのグローバルがある場合:

(defvar *db* nil)

...そして、REPLでそれをいじり始めます-追加、削除など-しかし、そのdefvarフォームを含むソースファイルに変更を加え、そのファイルをリロードしても消去されず*db*、すべての変更が消去されません作られた... 私はそれがそうであるように信じていsetqますdefparameter。より経験豊富な Lisper が間違っている場合は修正してください。

于 2010-10-04T14:03:23.773 に答える