#:g1: setfで自己代入

Posted 2018-12-19 20:05:20 GMT

Lisp SETF Advent Calendar 2018 20日目 》

今回は、Pythonや、Rubyにある自己代入構文をsetfで再現してみようかなと思います。

自己代入構文とは

a = a + ba += bと書ける構文ですが、起源はCなのでしょうか。
aが二回評価されないので効率が良いなんていう話もあるようです。

setfで再現してみる

それではとりあえず、(setf (op a) b)の形式で定義してみましょう。
下記では、標準のオペレーターと名前が被るのでパッケージを別にしています。

(defpackage :self-assignment
  (:use :cl)
  (:shadow "+" "-" "/" "//" "OR" "AND"))

(in-package :self-assignment)

(defmacro define-self-assignment-setf (op fn) `(define-setf-expander ,op (place) (multiple-value-bind (dv v sv setter getter) (get-setf-expansion place) (values dv v sv `(let ((,@sv (,',fn ,getter ,@sv))) ,setter) getter))))

(define-self-assignment-setf + cl:+) (define-self-assignment-setf - cl:-) (define-self-assignment-setf / cl:/) (define-self-assignment-setf // cl:floor) (define-self-assignment-setf % cl:mod) (define-self-assignment-setf or cl:or) (define-self-assignment-setf and cl:and)

試してみる

(let ((a 100) (b 42))
  (setf (+ a) b) a)
→ 142 

(let ((a 100) (b 42)) (setf (- a) b) a) → 58

(let ((a 100) (b 42)) (setf (/ a) b) a) → 50/21

(let ((a 100) (b 42)) (setf (// a) b) a) → 2

(let ((a 100) (b 42)) (setf (% a) b) a) → 16

(let ((a 100) (b 42)) (setf (or a) b) a) → 100

(let ((a (list (list 100))) (b 42)) (setf (and (caar a)) b) a)((42))

まとめ

Common Lispにはincfdecfがありますが、自己代入のようなものは、(setf place)で考えた場合、placeっぽくないので、define-modify-macroで定義するのがCommon Lisp流だなあ、と作ってみてから思ったりです。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus