lambdaを生成するリーダマクロ
arcのマクロ(gaucheにもあった?)
(fn (_) (if (odd _) (+ _ 10))
とかを
[if (odd _) (+ _ 10)]
でかけるやつ。
がちょっと便利そうだったので、CommonLispで作ってみた。
(defun deep-find (item seq &key (test #'eql)) (if (not (null seq)) (or (if (funcall test item (car seq)) item (if (not (atom (car seq))) (deep-find item (car seq) :test test))) (deep-find item (cdr seq) :test test)))) (defmacro >str (&body body) `(write-to-string ,@body)) (defmacro >sym (str &optional package) `(intern ,str ,package)) (defmacro str+ (&rest b) `(concatenate 'string ,@b)) (set-macro-character #\] (get-macro-character #\))) (set-dispatch-macro-character #\# #\[ #'(lambda (stream char1 char2) (let ((body (read-delimited-list #\] stream t))) (if (deep-find '_ body) `(function (lambda (_) ,body)) `(function (lambda ,(nreverse (do ((i 0 (1+ i)) (var nil)) ((> i 9) var) (if (deep-find (>sym (str+ "_" (>str i)) *package*) body) (setq var (cons (>sym (str+ "_" (>str i)) *package*) var))))) ,body))))))
deep-findはfindのdeep版で、あとは名前でわかるだろう。
「_(アンダーバー)」だけだと引数が二つ以上取れないので、勝手に「_0 〜 _9」まで追加した。
以下のように展開する。
#[+ 1 2] => #'(lambda () (+ 1 2)) #[+ _ 1] => #'(lambda (_) (+ _ 1)) #[if (< _0 10) (+ _1 2) (+ (- 100 _0) _2)] => #'(lambda (_0 _1 _2) (if (< _0 10) (+ _1 2) (+ (- 100 _0) _2)))
テスト
> (funcall #[+ 10 100]) ; 110 > (funcall #[+ _ 100] 1) ; 101 > (mapcar #[list _0 _1 _2] '(1 2 3) '(4 5 6) '(7 8 9)) ;((1 4 7) (2 5 8) (3 6 9))
うまくいっているようだ。