2015-05-04-01-tupleにして、あとwithマクロ - project-enigma

2015-05-04-01-tupleにして、あとwithマクロ

>> Site top >> weblog >> 月別アーカイブ >> 2015年05月のlog >> 2015-05-04-01-tupleにして、あとwithマクロ

最終更新日付:2015/05/04 13:25:29


tupleにして、あとwithマクロ

2015 年 05 月 04 日

結局、binder という名前はヤメて tuple にした。でもって、まだ適当命名だけれども手短かに with というマクロを導入。

これは、要するに『指定した任意の数のシンボルについて、いちいち get 関数を明示的に記述しなくても済むようにする』ためのマクロだ。いつもどおり、まずは定義から。

(defmacro with ((&rest syms) &rest body)
  (let ((prefixes (mapcar (lambda (sym)
                            (concatenate 'string (symbol-name sym) ".")) syms)))
    (labels ((onlisp/flatten (x &optional (acc nil))
               (cond ((null x) acc)
                     ((atom x) (cons x acc))
                     (t (onlisp/flatten (car x) (onlisp/flatten (cdr x) acc)))))
             (ref-symbolp (x)
               (when (and (symbolp x) (not (keywordp x)))
                 (let* ((name (symbol-name x))
                        (pos  (position #\. name)))
                   (when pos
                     (and (< 0 pos) (< pos (1- (length name)))
                          (member (subseq name 0 (1+ pos)) prefixes :test #'string=))))))
             (make-symbol-macrolet (sym)
               (let* ((name (symbol-name sym))
                      (pos  (position #\. name))
                      (obj  (intern (subseq name 0 pos)))
                      (key  (intern (subseq name (1+ pos)) :keyword)))
                 `(,sym (tuple:get ,obj ,key)))))
      (let ((syms (remove-duplicates
                   (remove-if-not #'ref-symbolp (onlisp/flatten body)))))
        `(symbol-macrolet ,(mapcar #'make-symbol-macrolet syms)
           ,@body)))))

 

このマクロは、まぁ良くある with 系マクロと同じように使う。たとえばキーとして :x と :y を持つ(座標を示す)tuple である pt1 および pt2 があるとして、get を使う場合は以下のようにして2点間の距離を取ることになる。

(sqrt (+ (expt (- (tuple:get pt1 :x) (tuple:get pt2 :x)) 2)
         (expt (- (tuple:get pt1 :y) (tuple:get pt2 :y)) 2)))

 

これが、with を使うと以下のように書ける。

(tuple:with (pt1 pt2)
  (sqrt (+ (expt (- pt1.x pt2.x) 2)
           (expt (- pt1.y pt2.y) 2))))

 

上記の with の使用は、以下のように展開される。symbol-macrolet を使用して get 呼び出しへと展開しているだけなので、上手い具合に setf さえもそのまま使える。

(symbol-macrolet ((pt1.x (tuple:get pt1 :x))
                  (pt2.x (tuple:get pt2 :x))
                  (pt1.y (tuple:get pt1 :y))
                  (pt2.y (tuple:get pt2 :y)))
  (sqrt (+ (expt (- pt1.x pt2.x) 2)
           (expt (- pt1.y pt2.y) 2))))

 

まぁ、こんなところかな。どれくらい使うかわからないし、汎用的に使うものとしての堅牢さをちゃんと備えているかも正直不安だ。まぁでもひとまず。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : Common Lisp

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.