web-dev-qa-db-ja.com

defvar、defparameter、setf、setqの違い

同様の質問 が見つかりました。

しかし、その説明はよくわかりません。

だから私は次の例でclispを実行しようとしています:

  [1]> (defvar a 5)
  A
  [2]> (+ a 1)
  6
  [3]> (defparameter b 5)
  B
  [4]> (+ b 1)
  6
  [5]> (setf c 5)
  5
  [6]> (+ c 1)
  6
  [7]> (setq d 5)
  5
  [8]> (+ d 1)
  6
  [9]> (let ((a 500)) (+ a 1))
  501
  [10]> (let ((b 500)) (+ b 1))
  501
  [11]> (let ((c 500)) (+ c 1))
  501
  [12]> (let ((d 500)) (+ d 1))
  501
  [13]> 

私が見つけたものは全く同じです。

彼らと何が違うのか分かりませんか?

41
sam

DEFPARAMETERは常に値を割り当てます。そう:

[1]> (defparameter a 1)
A
[2]> (defparameter a 2)
A
[3]> a
2

dEFVARは一度だけそれを行うので、

[4]> (defvar b 1)
B
[5]> (defvar b 2)
B
[6]> b
1

SETFは、SETQを内部で使用するマクロですが、より多くの可能性があります。ある意味では、より一般的な代入演算子です。例えば。 SETFでできること:

[19]> (defparameter c (list 1 2 3))
[21]> (setf (car c) 42)                                              
42
[22]> c
(42 2 3)

しかし、SETQではそれを行うことはできません。

[23]> (setq (car c) 42)                                              
*** - SETQ: (CAR C) is not a symbol
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead.
ABORT          :R2      Abort main loop
Break 1 [24]> abort
65

defvardefparameterはどちらも、変数を「動的スコープ変数」として宣言します。さらに、defparameterは常に、変数の値を2番目の引数として渡した値に設定します。これはdefvarとは異なり、以前に設定されていない場合にのみ変数の値を設定します。

グローバル字句スコープでsetfまたはsetqを使用して変数を定義することは定義されていません。一部の実装では、動的にスコープされる変数が作成されますが、作成されないものもあります。初めて実行すると、診断メッセージが表示される場合があります。

字句スコープと動的スコープの変数の違いを理解するには、次のコードスニペットを試してください。

* (defvar *a* 1)

*A*
* (let ((*a* 5)) (defun demo-a () *a*))

DEMO-A
* (let ((b 5)) (defun demo-b () b))

DEMO-B
* (let ((*a* 100)) (demo-a))

100
* (let ((b 100)) (demo-b))

5

ここでは、動的スコープの変数と値を返す関数を作成します(関数の作成中に値が異なるバインディング内で定義されます。これは必要ではなく、bの字句閉包に似ているためだけに行われます)。次に、新しい変数を定義し、その値を返す関数を定義します。

その後、同じ名前の変数に値をバインドするクロージャ内で両方の関数を呼び出します。動的スコープの場合、同じ変数です。字句閉包のケース(b)では、2つの異なる字句閉包で定義されているため、名前は同じですが、同じ変数ではありません。

setfsetqの違いについては、常にsetfを使用するようにしてください((setq blah blahblah)は機能し、(setf blah blahblah)は同じことをしません)。

25
Vatine