結城浩のSICP日記 RSSフィード

2006-05-15

デバッグプリント デバッグプリント - 結城浩のSICP日記 を含むブックマーク

ひ日記: dot.gaucheの中身おまけひげぽんさんの日記を読んで。

もしかしたら、デバッグプリントってdefine-syntaxで作ればよいのでは?と思いました。

(define-syntax debug
  (syntax-rules ()
    ((debug var)
      (begin
        (display 'var)
        (display " = ")
        (display var)
        (newline)))
    ((debug var0 var1 ...)
      (begin
        (display 'var0)
        (display " = ")
        (display var0)
        (display ", ")
        (debug var1 ...)))
    ((debug)
      (newline))))

;以下テスト用
(define x 123)
(define y '(a b c))
(debug x)
(debug y)
(debug (cdr y))
(debug x y (cdr y))

実行結果です。

x = 123
y = (a b c)
(cdr y) = (b c)
x = 123, y = (a b c), (cdr y) = (b c)

式と値を並べて表示できるので、なかなか良い、でしょでしょ?

ちなみに、こんなのも。

(debug debug)       ;debug = #<macro debug>と表示
(debug lambda)      ;lambda = #<syntax lambda>と表示

syntax-rules syntax-rules - 結城浩のSICP日記 を含むブックマーク

R5RSの「導出式型」を読んでいます。

というか、syntax-rulesのサンプルを求めてさまよっているのです。

見よう見まねでmy-ifを作りました。

(define-syntax my-if
  (syntax-rules ()
    ((my-if test then)
      (cond (test then)))
    ((my-if test x y)
      (cond (test x)
            (else y)))))

(my-if)
; *** ERROR: Compile Error: malformed my-if: (my-if)

(my-if #t "Yes")
;=> Yes

(my-if #f "Yes")
;=> #<undef>

(my-if #t "Yes" "No")
;=> Yes

(my-if #f "Yes" "No")
;=> No

define-class define-class - 結城浩のSICP日記 を含むブックマーク

Gaucheマニュアルの「2.5 統合されたオブジェクトシステム 」を読んでいます。

見よう見まねで作ったクラス。

(define-class person ()
  ((name :init-value "John Doe")
   (age :init-value 0)))

(define-method set-name! ((p person) name)
  (set! (slot-ref p 'name) name))

(define-method set-age! ((p person) age)
  (set! (slot-ref p 'age) age))

(define-method write-object ((p person) port)
  (format port "[person \"~a\" (~a)]"
          (slot-ref p 'name)
          (slot-ref p 'age)))

(define p (make person))
(set-name! p "Alice")
(set-age! p 23)
(write-object p (standard-output-port))

実行結果です。

[person "Alice" (23)]

追記:ruiさんからコメントでご教示いただきました。ありがとうございます。

  • クラス名を < ... > でくくるのが普通
  • :setterスロットオプションを使うこともできる
  • write-objectの出力フォーマットを#<<person> ... >とすることが多い
(define-class <person> ()
  ((name :init-value "John Doe" :setter set-name!)
   (age :init-value 0 :setter set-age!)))

(define-method write-object ((p <person>) port)
  (format port "#<<person> \"~a\" (~a)>"
          (slot-ref p 'name)
          (slot-ref p 'age)))

(define p (make <person>))

(set-name! p "Alice")

(set-age! p 23)

(write-object p (standard-output-port))

実行結果です。

#<<person> "Alice" (23)>

さらに、(set! (ref obj 'slot) value)を使う形式も書いてみます。

(define-class <person> ()
  ((name :init-value "John Doe")
   (age :init-value 0)))

(define-method write-object ((p <person>) port)
  (format port "#<<person> \"~a\" (~a)>"
          (slot-ref p 'name)
          (slot-ref p 'age)))

(define p (make <person>))

(set! (ref p 'name) "Alice")

(set! (ref p 'age) 23)

(write-object p (standard-output-port))

追記:ruiさんからコメントで情報をいただきました。なるほど。

ruirui2006/05/15 02:21:setterスロットオプションを渡すと、セッターのジェネリック手続きを生成することができます。こんなふうに。
(define-class <person> ()
((name :init-value "John Doe" :setter set-name!)
(age :init-value 0 :setter set-age!)))
set-name!やset-age!を自分で定義する必要がなくなります。ただし、最近はrefをつかって、(ref obj 'slot)や(set! (ref obj 'slot) new-value)などと書いて、あまり自前のアクセッサを定義しないスタイルをよく見るような気がしますね。
なお、クラスの名前は"<"で始めて">"で終わるようにするのが一般的です。また、write-objectで書き出す表現は、"#<<class-name> ...>"というようにするのが慣習のようです。

ruirui2006/05/15 10:23ちなみに #< ... > というは、read手続きで読み込めないオブジェクトを出力するときに使う表現です。表現そのものはおそらく CommonLispの http://www.lisp.org/HyperSpec/Body/mac_print-unr_dable-object.html が元です。
readで読める表現というのもあり、SRFI-10で定義されている #,(...) という表現を使います。Gaucheのマニュアルでは「読み込み時コンストラクタ」という章がその説明になっています。もし興味があれば読んでみてください。

higeponhigepon2006/05/15 19:00>define-syntax
お。こんなことができるんですねぇ。
なるほどなるほど。
確かにこちらのほうがスマートな気がしますね。

hisahisa2006/05/15 20:45define-syntax で書いた debug いいですね!
mzschemeとpetiteでも使えました。いただきます!
petiteにはdebugという名前の組み込み関数がすでにあったりするので、処理系によっては名前を変えた方がいいかもしれません。