public_notes/content/20240428122916 setf setq defvar defparameter の違い.md

106 lines
No EOL
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 20240428122916 setf setq defvar defparameter の違い
#common_lisp #tips
若干ややこしいく、前提となる知識が必要だが、
- defvar, defparameterはダイナミックスコープの変数(スペシャル変数)を定義する。
- letや未定義時のsetfはレキシカルスコープの変数を定義する。
グローバル変数をレキシカルに作る方法は(少なくとも標準的には)無い。 上記未定義時のsetfは期待した動作をするが、推奨されていない。
cf.
- [[20240428125606 ダイナミックスコープ レキシカルスコープ |ダイナミックスコープ レキシカルスコープ]]
- [lispguide.xml](https://google.github.io/styleguide/lispguide.xml?showone=Global_variables_and_constants#Global_variables_and_constants)
Common Lisp does not have global lexical variables
- defvar: 値が定義されていなければ定義する。上書きしようとしても存在している場合はできない(というか無視される)。
- defparameter: 定義されているかに関係なく定義する。すでに定義されていたら上書きする。
- defconstant: 定数を定義する。複数回定義しようとしたり、値を変更しようとするとエラーになる。
- setf: 定義された変数を変更するために使う。定義されていない変数を変更しようとすると、新規作成され、警告が出る。
- setq: 使わなくてよい。 setfはsetqの上位互換。
以下sbclをslimeで動かした時の、replでの実行結果
```lisp
;; 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 できた
```
### その他
識別子に使用可能な文字は? [変数](https://wisdom.sakura.ne.jp/programming/lisp/clisp5.html) には、
>変数名には、アルファベット、数値、記号で構成される識別子を指定します。 Common Lisp の識別子は、アルファベットの大文字小文字を区別しません。 次の記号を使うこともできます。
> + - * / @ $ % ^ & _ = < > ~ .
>
> ただし +1 や -20 という表記は整数として認識できるため識別子にはなりません。 +$ や 1+ は識別子として認識することができます。
と書かれている。
### Refs.
- [Lispで変数宣言する方法 -](https://minegishirei.hatenablog.com/entry/2023/04/17/094001)
- [Lispのダイナミック・スコープとスペシャル変数 · wshito's diary](https://diary.wshito.com/comp/lisp/dynamic-scope/)
- [「対話によるCommon Lisp入門」第2話まとめート · wshito's diary](https://diary.wshito.com/comp/lisp/cl-dialogue/2/)
- [lisp - What's difference between defvar, defparameter, setf and setq - Stack Overflow](https://stackoverflow.com/a/8928038)