2016-05-28-01-CL-OPERATORにreferenceを追加しようかな - project-enigma

2016-05-28-01-CL-OPERATORにreferenceを追加しようかな

>> Site top >> weblog >> 月別アーカイブ >> 2016年05月のlog >> 2016-05-28-01-CL-OPERATORにreferenceを追加しようかな

最終更新日付:2016/05/28 16:12:08


CL-OPERATORにreferenceを追加しようかな

2016 年 05 月 28 日

今回は、CL-OPERATOR に導入しようかと思いついた reference 機能について。これは C++ における「パラメータの参照渡し」を模倣するものだ。こんなことをしようとすると Lisper の方々に取り囲まれて四方からマサカリを投げ付けられてしまうかもしれない。「多値が返せるのに、パラメータ渡ししたモノを変更する仕組みなんて!」というワケだな。わかってますって。使わなければ害はないモノなのだし、まだ導入すると決まったワケでもない。とにかく書くだけ書いておこう。

まず、参照を表現するクラスを導入する。以下のような reference クラスだ。

(defclass reference ()
  ((accessor :type     cl:function
             :initform nil
             :initarg  :accessor
             :accessor reference-accessor)))

 

そして、これはキレイではないのだが、関数やメソッドに「参照として」パラメータを渡す際に使用する ref マクロ。クロージャひとつで値の取得と設定をできるようにする、お決まりのパターンだ。

(defmacro ref (place)
  (let ((g-isget  (gensym "ISGET"))
        (g-newval (gensym "NEWVAL")))
    `(make-instance 'reference
                    :accessor (lambda (&optional (,g-newval ',g-isget))
                                (unless (eq ,g-newval ',g-isget)
                                  (setf ,place ,g-newval))
                                ,place))))

 

最後に、渡された参照パラメータの利用を簡単にするためのいくつかの関数とマクロ。

(defun reference-access (ref)
  (funcall (reference-accessor ref)))

(defun (setf reference-access) (newval ref)
  (funcall (reference-accessor ref) newval)
  newval)

(defmacro with-reference ((&rest symbols) &body bodies)
  (labels ((make-refsym (symbol)
             (onlisp/symb symbol "&"))
           (make-macrolet (symbol refsym)
             `(,symbol (reference-access ,refsym))))
    (let ((refsyms (mapcar #'make-refsym symbols)))
      `(locally (declare (type reference ,@refsyms))
         (symbol-macrolet ,(mapcar #'make-macrolet symbols refsyms)
           ,@bodies)))))

 

最後の with-reference マクロについては、使い方の例を示した方が良いだろう。参照パラメータを使用する関数では、参照として扱うパラメータ名の末尾に & を付ける。そして、処理の内部を with-reference で囲む。この内部では & なしの名前を使うことができる。以下のような感じ。

(defun test-swap (a& b&)
  (with-reference (a b)
    (let ((tmp a))
      (setf a b)
      (setf b tmp)))
  nil)

(let ((a 0)
      (b 9))
  (test-swap (ref a) (ref b))
  (values a b))

;=> 9
;   0

 

個人的に気に入らないのは、パラメータ渡しをする側で (ref a) などとしなければならないことだ。だが、C++ 界隈(の一部)で参照を嫌う向きがあるのは、呼び出し元での記述の仕方関して値渡しと参照渡しの区別がつかないことに理由があると思う。なので、参照渡しであることを明示しなければならないのは、ひょっとしたら良いことなのかもしれない。

ところで、CL-OPERATOR には fixed-pointer というモノがある。要するに固定ポインタで、内部的にはやっていることは同じで、シンタックスが違うだけということになる。冒頭で『まだ導入すると決まったワケでもない』と書いたのはこれが理由だ。そもそも参照渡しのサポートを思いついたきっかけは fixed-pointer に感じた違和感なのだ。「移動できないのにポインタって変かもな。というか、コレって参照じゃね?」と思ったのが最初で、それからしばらくは fixed-pointer を参照に置き換えてしまおうかと思っていたのだ。そのあとごちゃごちゃと考えた揚げ句、fixed-pointer は存続として、別途 reference を用意するという方向になっている。今のところは。

 

コメント

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

 

このページのタグ

Page tag : Common Lisp

 

 


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