20240428122916 setf setq defvar defparameter の違い

common_lisptips

若干ややこしいく、前提となる知識が必要だが、

  • defvar, defparameterはダイナミックスコープの変数(スペシャル変数)を定義する。
  • letや未定義時のsetfはレキシカルスコープの変数を定義する。

グローバル変数をレキシカルに作る方法は(少なくとも標準的には)無い。 上記未定義時のsetfは期待した動作をするが、推奨されていない。

cf.

  • ダイナミックスコープ レキシカルスコープ

  • lispguide.xml Common Lisp does not have global lexical variables

  • defvar: 値が定義されていなければ定義する。上書きしようとしても存在している場合はできない(というか無視される)。

  • defparameter: 定義されているかに関係なく定義する。すでに定義されていたら上書きする。

  • defconstant: 定数を定義する。複数回定義しようとしたり、値を変更しようとするとエラーになる。

  • setf: 定義された変数を変更するために使う。定義されていない変数を変更しようとすると、新規作成され、警告が出る。

  • setq: 使わなくてよい。 setfはsetqの上位互換。

以下sbclをslimeで動かした時の、replでの実行結果

;; defvar
(defvar b 100) ; 1回目
b; => 100
(defvar b 200) ; 2回目
b; => 100      ; とくにwarningなども出ず、シンプルに無視された
 
(setf b 300)  ; setfで変えられるの?
b ; => 300    ; 変えらえる
 
;; defparameter
(defparameter c 1) ; 1回目
c ; => 1
(defparameter c 2) ; 2回目
c ; => 2 ; 変わっている
 
(setf c 3) ; setfで変えられるの?
c; => 3 ; 変えられる
 
;; defconstant 定数を宣言
(defconstant d 1); 1回目
d ; => 1
(defconstant d 2); 2回目 エラー
;; The constant D is being redefined (from 1 to 2)
;;    [Condition of type DEFCONSTANT-UNEQL]
;; See also:
;;   Common Lisp Hyperspec, DEFCONSTANT [:macro]
;;   SBCL Manual, Idiosyncrasies [:node]
(setf d 3) ; setfしてみる これもエラー
;; Execution of a form compiled with errors.
;; Form:
;;   (SETQ D 3)
;; Compile-time error:
;;   D is a constant and thus can't be set.
;;    [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
;; 定義されていない値をsetf
(setf e 1) ; => warning
;; in: SETF E
;;     (SETF E 1)
;; 
;; caught WARNING:
;;   undefined variable: COMMON-LISP-USER::E
;; 
;; compilation unit finished
;;   Undefined variable:
;;     E
;;   caught 1 WARNING condition
(setf e 2); 2回目 ;; 同様のwarningが出る
e ; => 2
 
;; defconstant強い。letでも変えられない
(let
    ((d 2)))
;; Execution of a form compiled with errors.
;; Form:
;;   (LET ((D 2)))
;; Compile-time error:
;;   COMMON-LISP-USER::D names a defined constant, and cannot be used in LET.
;;    [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
 
 
;; こう書いた場合もグローバルにアクセスできる?
;; ダイナミックスコープならできるはず
(defun nazo ()
  (defvar nazo_value 10))
(nazo)
nazo_value ; => 10 できた

その他

識別子に使用可能な文字は? 変数 には、

変数名には、アルファベット、数値、記号で構成される識別子を指定します。 Common Lisp の識別子は、アルファベットの大文字小文字を区別しません。 次の記号を使うこともできます。

      • / @ $ % ^ & _ = < > ~ .

ただし +1 や -20 という表記は整数として認識できるため識別子にはなりません。 +$ や 1+ は識別子として認識することができます。

と書かれている。

Refs.