#:g1: setf可能な場所なのかどうかを確認したい

Posted 2020-09-23 02:00:36 GMT

setf可能な場所なのかどうかを確認したい、というのは、そもそもどういう動機からなのかというと、身近な例では、(setf nthcdr)等と書いた時に、

(let ((x (list 0 1 2 3 4)))
  (setf (nthcdr 1 x) (list 'a 'b 'c))
  x)
!Error: Undefined operator (setf nthcdr) in form ((setf nthcdr) #:|Store-Var-34450| #:g34451 #:g34452).

となってしまい、あれ、(setf nthcdr)って設定されてないんだっけ?というようなことを防止したい、というような動機です。

上記の場合、

(let ((x (list 0 1 2 3 4)))
  (setf (cdr (nthcdr 0 x)) (list 'a 'b 'c))
  x)(0 a b c)

と書き直せば良いのですが。

考えられそうなアプローチ

等々、色々ありますが、まず、setfして回るのは、処理系を改造することになるので、ちょっと嫌なのと、やるとしてもsetfの展開方法が処理系ごとに結構違っているので、setfを設定するコードの可搬性を担保するのが結構難しい。

次に、ユーティリティマクロで囲んだり、setfの類似品を作る的なところですが、この問題をコードウォークして解決するとしても、局所関数/マクロでsetfを定義できたりするので結構大変でしょう。

標準規格で定義されているsetfの場所以外のものは一切書かない、というのは若干寂しいですが、これはこれでありかなと思います。

標準の(setf place)を一覧にする

標準の(setf place)を全部把握したい、ということで、CLHS: 5.1.2 Kinds of Placesで定義されているものを、列記してみます。

変数名全部

これは問題ないでしょう

標準定義の関数フォーム形式

(setf bit)
(setf c[ad]+r) ;car cdr系全部
(setf char)
(setf class-name)
(setf compiler-macro-function)
(setf documentation)
(setf elt)
(setf fdefinition)
(setf fifth)
(setf fill-pointer)
(setf find-class)
(setf first ... tenth) ; firstからtenthまで
(setf rest)
(setf get)
(setf getf)
(setf gethash)
(setf ldb)
(setf logical-pathname-translations)
(setf macro-function)
(setf mask-field)
(setf nth)
(setf readtable-case)
(setf row-major-aref)
(setf sbit)
(setf schar)
(setf slot-value)
(setf subseq)
(setf svref)
(setf symbol-function)
(setf symbol-plist)
(setf symbol-value)

Apply との組み合わせ

上記に加えて、Applyのフォームと組合せ可能なものとして、arefbitsbitがあるので、

(setf (apply #'aref))
(setf (apply #'bit))
(setf (apply #'sbit))

Values との組み合わせ

上記の関数フォームに組合せ可能なものとして更にvalues

(setf values)

the との組み合わせ

さらに組合せ可能なものとして、the

(setf the)

setf系マクロ

decf pop pushnew incf push remf あたりのマクロですが、define-modify-macroで定義したように動くので、valuesと組合せて使うことは想定されていない様子。
LispWorksに至ってはエラーになります。

まとめ

標準の組み合わせだけでも、結構複雑な組み合わせは可能です。

(let ((ba (make-array '(4 4) 
                      :element-type 'bit 
                      :initial-element 1))
      (bb (make-array '(4 4) 
                      :element-type 'bit 
                      :initial-element 1)))
  (setf (values (the bit (apply #'bit ba '(0 0)))
                (the bit (apply #'bit bb '(0 0)))) 
        (values 0 0))
  (values ba bb))
→ #2A((0 1 1 1) (1 1 1 1) (1 1 1 1) (1 1 1 1))
  #2A((0 1 1 1) (1 1 1 1) (1 1 1 1) (1 1 1 1))

(let ((a (make-array '(4 4) :initial-element 0))) (incf (the integer (apply #'aref a '(1 1)))) a) → #2A((0 0 0 0) (0 1 0 0) (0 0 0 0) (0 0 0 0))

便利なsetfマクロですが、あまり複雑なことはしない方が良いのかなと(月並)
ただ、(setf values)については、色々なソースを眺めていても、あまり活用されていない気がするので、もっと活用されても良いかなと思います。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus