Gauche:スロットアクセスの記述の短縮
スロットアクセスの記述の短縮
- クラスのインスタンスのスロットにアクセスする場合、通常は、
(slot-ref instance 'slot-name) のように書きますが、
これを短く記述するためのマクロ (with-slot-accessors) を作ってみました。
- 変数を スロット自体に束縛するのではなく、アクセサに束縛するようになっているため、
スロットの値を参照するときは、(slot-name) のように括弧で囲う必要があります。
また、スロットに値を格納する場合は、(set! (slot-name) value) のように書きます。
- 普通に (slot-ref instance 'slot-name) と書いた場合に比べると、
ある程度のオーバーヘッドがあります(実行結果参照)。
(use gauche.time)
;; with-slot-accessors
;; binds variables to slot-accessors.
;; (with-slot-accessors (binding ...) instance body ...)
;; where binding is var or (var slot-name) .
(define-syntax with-slot-accessors
(syntax-rules ()
[(_ (binding ...) instance body ...)
(%with-slot-accessors () (binding ...) instance body ...)]))
(define-syntax %with-slot-accessors
(syntax-rules ()
[(_ (new-binding ...) () instance body ...)
(let (new-binding ...) body ...)]
[(_ (new-binding ...) ((var slot-name) rest ...) instance body ...)
(%with-slot-accessors
(new-binding ... (var (getter-with-setter
(lambda () (slot-ref instance 'slot-name))
(lambda (v) (slot-set! instance 'slot-name v)))))
(rest ...) instance body ...)]
[(_ (new-binding ...) (var rest ...) instance body ...)
(%with-slot-accessors
(new-binding ... (var (getter-with-setter
(lambda () (slot-ref instance 'var))
(lambda (v) (slot-set! instance 'var v)))))
(rest ...) instance body ...)]))
;; ***** examples *****
(define-class <3d-point> () (x y z))
(define p1 (make <3d-point>))
(define p2 (make <3d-point>))
(with-slot-accessors (x y z) p1
(with-slot-accessors ([x2 x] [y2 y] [z2 z]) p2
(set! (x) 10)
(set! (y) 20)
(set! (z) 30)
(set! (x2) 100)
(set! (y2) 200)
(set! (z2) 300)))
(with-slot-accessors (x y z) p1
(with-slot-accessors ([x2 x] [y2 y] [z2 z]) p2
(format #t "p1=(~d ~d ~d)~%" (x) (y) (z))
(format #t "p2=(~d ~d ~d)~%" (x2) (y2) (z2))))
;; ***** measure time *****
(define-syntax print-time
(syntax-rules ()
((_ msg n body ...)
(print msg (time-this n (lambda () body ...))))))
(define n 1000000)
(print)
(print-time "slot-ref : " n (list (slot-ref p1 'x) (slot-ref p1 'y) (slot-ref p1 'z)))
(print-time "ref : " n (list (ref p1 'x) (ref p1 'y) (ref p1 'z)))
(print-time "~ : " n (list (~ p1 'x) (~ p1 'y) (~ p1 'z)))
(with-slot-accessors (x y z) p1
(print-time "this-1 : " n (list (x) (y) (z))))
(print-time "this-2 : " n (with-slot-accessors (x y z) p1
(list (x) (y) (z))))
(print)
(print-time "slot-set! : " n (slot-set! p1 'x 1000)
(slot-set! p1 'y 2000)
(slot-set! p1 'z 3000))
(print-time "set! ref : " n (set! (ref p1 'x) 1000)
(set! (ref p1 'y) 2000)
(set! (ref p1 'z) 3000))
(print-time "set! ~ : " n (set! (~ p1 'x) 1000)
(set! (~ p1 'y) 2000)
(set! (~ p1 'z) 3000))
(with-slot-accessors (x y z) p1
(print-time "set! this-1: " n (set! (x) 1000)
(set! (y) 2000)
(set! (z) 3000)))
(print-time "set! this-2: " n (with-slot-accessors (x y z) p1
(set! (x) 1000)
(set! (y) 2000)
(set! (z) 3000)))
- Gauche 0.9.9 での実行結果 (OS: Windows)
p1=(10 20 30) p2=(100 200 300) slot-ref : #<time-result 1000000 times/ 0.348 real/ 0.297 user/ 0.047 sys> ref : #<time-result 1000000 times/ 0.984 real/ 1.063 user/ 0.062 sys> ~ : #<time-result 1000000 times/ 1.209 real/ 1.297 user/ 0.047 sys> this-1 : #<time-result 1000000 times/ 0.391 real/ 0.391 user/ 0.047 sys> this-2 : #<time-result 1000000 times/ 0.891 real/ 1.108 user/ 0.141 sys> slot-set! : #<time-result 1000000 times/ 0.203 real/ 0.203 user/ 0.000 sys> set! ref : #<time-result 1000000 times/ 0.844 real/ 0.860 user/ 0.031 sys> set! ~ : #<time-result 1000000 times/ 1.078 real/ 1.141 user/ 0.032 sys> set! this-1: #<time-result 1000000 times/ 0.309 real/ 0.312 user/ 0.000 sys> set! this-2: #<time-result 1000000 times/ 0.822 real/ 1.078 user/ 0.156 sys>
hamayama(2020/08/04 08:25:45 UTC)(2020/08/06 01:57:03 UTC)
Shiro(2020/08/05 03:25:57 UTC): 速いのはいいですね。Common Lispにwith-slotsがありますが、
identifier macroがあれば実装できるんだよな…