オブジェクトっぽく書きたい!
javascriptでは変数に対するメソッドを 変数.メソッド で指定できる。メソッドを定義するのは面倒だが、今回はメソッドにこだわる必要はなく、関数でよい。このような書式の方がデータが見やすくなるので、lispでも使えたら便利だろうと思う。
; オブジェクトベースな書式 foo.bar(fizz).buzz(hoge,fuga).magi ; 対応するlisp (magi (buzz (bar foo fizz) hoge fuga))
上の書式では、fooのデータに対して処理を行うことが明白であるが、下の書式では中心となるデータが何だか良く分からない。そこで、ちょっと細工をしてデータが主体になる書式に対応させたい。lispではリーダマクロを使うとそれが行える。
#[foo bar] -> (bar foo) ; 基本 #[foo (bar fizz) (buzz hoge fuga) magi] -> (magi (buzz (bar foo fizz) hoge fuga)) ; 例題
こんな感じに展開される。望むなら、入れ子に使うこともできる。
; 例題 #[foo (bar fizz) #[hoge (buzz fuga)] magi] #[foo #[fizz bar] #[hoge (buzz fuga)] magi] #[foo #[fizz bar] #[hoge #[fuga buzz]] magi]
このリーダマクロの定義は次のようである。
(defun \[-reader (stream char param) (let* ((*readtable* *\[-readtable*)) (let* ((obj (read-delimited-list #\] stream nil)) (l (car obj))) (dolist (a (cdr obj) l) (setq l (if (consp a) (apply 'list (car a) l (cdr a)) (list a l))))))) (set-dispatch-macro-character #\# #\[ #'\[-reader) ; set-dispatch-macro-characterの後に定義する。 (defvar *\[-readtable* (let ((readtable (copy-readtable))) (set-macro-character #\] #'(lambda (stream c) #\]) nil readtable) readtable))
; テスト用関数 (defun test (str) (read (make-string-input-stream str))) (test "#[foo bar]") | (bar foo) (test "#[foo (bar fizz) (buzz hoge fuga) magi]") | (magi (buzz (bar foo fizz) hoge fuga)) (test "#[foo (bar fizz) #[hoge (buzz fuga)] magi]") | (magi (buzz (bar foo fizz) hoge fuga)) (setq x '(1 2 3 (4 (5)))) (progn #[x cadddr cadr car]) | 5 (progn #[x (elt 3) (elt 1) car]) | 5 ; マクロなのでsetfで利用可能。 (progn (setf #[x (elt 3) (elt 1) car] t) x) | (1 2 3 (4 (t))) ; 関数でなくても良い。 '#[1 2 3 4 5 6] | (6 (5 (4 (3 (2 1))))) ; 天の邪鬼な使い方。 #[(#[9 x] #['- f]) #[#[f (funcall x 3)] let]] | 6