Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2011-06-08

CLでSRFI-10

| 19:47 | CLでSRFI-10 - わだばLisperになる を含むブックマーク はてなブックマーク - CLでSRFI-10 - わだばLisperになる

きまぐれでCLに移植しているSRFIですが今回は、#,外部フォームです。

#,はリーダーマクロですが、CLの#.(リード時EVAL)に似た感じのものです。

違いとしては、CLの#.は後続の式をEVALしますが、SRFI-10の#,はAPPLYします。

ちなみに、#,はCLtL1やZetalispでは、ロード時評価のリーダーマクロでしたがANSI CLでは、load-time-valueに置き換えられて廃止になっています。

下準備

(srfi-10:enable-read-time-application)
(import 'srfi-10:define-reader-ctor)
;; CLの #.
#.(+ 3 (+ (+ 3 3) 3))
;=> 12

;; SRFI-10の#,
(define-reader-ctor '+ #'+)

#,(+ 3 #,(+ #,(+ 3 3) 3))
;=> 12

そのままでは、式のCDRが再帰的に評価されていかないので#,をネストしたりする必要があります。

単純なリード時の評価以外の主な応用としては、構造体などの簡便な表記などがあるようです。

(defstruct foo x y z)

(define-reader-ctor 'foo #'make-foo)

#,(foo :x 1 :z 2 :y 3)
;=> #S(FOO :X 1 :Y 3 :Z 2)

(defstruct bar x y z)

(define-reader-ctor 'bar
  (lambda (x y z) (make-bar :x x :y y :z z)))

#,(bar 1 2 3)
;=> #S(BAR :X 1 :Y 2 :Z 3)

クラスにreader-ctorを定義して、さらにprint-objectも設定してみる

(defclass baz ()
  ((x :initarg :x)
   (y :initarg :y)
   (z :initarg :z)))

(define-reader-ctor 'baz
  (lambda (&rest args)
    (apply #'make-instance 'baz args)))

(defmethod print-object ((obj baz) stream)
  (with-slots (x y z) obj
    (format stream "#,(BAZ :x ~S :y ~S :z ~S)" x y z)))

#,(baz :x 1 :y 2 :z 3)
;=> #,(BAZ :x 1 :y 2 :z 3)

移植について

SRFI-10の文献だけだと微妙な挙動が分からなかったためGaucheで動作を確認して大体のところはGaucheと同じような挙動にしました。

関数のルックアップは、シンボルのSYMBOL-FUNCTIONを見るようにもしてみましたが、これもGaucheの動作に合せてdefine-reader-ctorで定義したものだけ引いてくるようにしてみました。

ちなみにctorはconstructorの略のようで、調べてみたらちらほら用例があるようです。catみたいな略し方ですね。