2010/09/08

syntax-rules: define-overload (clojure の defn みたいなもの)

引数の数にマッチして呼び出される本体が変わる clojure の defn を思い出したので書いてみました。
書いてみると別にどうということはありませんね・・・。

なぜ define-overload という名前かというと、始めて defn を見たときの感想が C# のオーバーロードっぽいなぁだったので。。かっこいい名前が思いつきませんでした。そういや syntax-rules にも似てますよね。

まずはマクロを書く前に展開イメージを
(use util.match)
;; image
(define-overload fact
((n)
(fact n 1))
((n acc)
(if (zero? n)
acc
(fact (- n 1)(* n acc)))))
;; expand image
(define (fact . args)
(match args
((n)
(fact n 1))
((n acc)
(if (zero? n)
acc
(fact (- n 1)(* n acc))))))
(fact 5)
;; 120
(use slib)
(require 'trace)
(trace fact)
(fact 5)
;; CALL fact 5
;; CALL fact 5 1
;; CALL fact 4 5
;; CALL fact 3 20
;; CALL fact 2 60
;; RETN fact 120
;; RETN fact 120
;; RETN fact 120
;; RETN fact 120
;; RETN fact 120
;; 120
(untrace fact)
(use liv.debugs)
(debug :fact)
(define (fact . args)
(match args
((n)
(dbg :fact ";; fact n = ~a" n)
(fact n 1))
((n acc)
(dbg :fact ";; fact n = ~a, acc = ~a" n acc)
(if (zero? n)
acc
(fact (- n 1)(* n acc))))))
(fact 5)
;; fact n = 5
;; fact n = 5, acc = 1
;; fact n = 4, acc = 5
;; fact n = 3, acc = 20
;; fact n = 2, acc = 60
;; fact n = 1, acc = 120
;; fact n = 0, acc = 120
120
view raw image.scm hosted with ❤ by GitHub

以下マクロ本体
(use util.match)
(define-syntax define-overload
(syntax-rules ()
((_ name ((arg ...) body ...) ...)
(define (name . args)
(match args
((arg ...) body ...) ...)))))
(macroexpand '(define-overload fact
((n)
(fact n 1))
((n acc)
(if (zero? n)
acc
(fact (- n 1)(* n acc))))))
;; (#<identifier user#define>
;; (fact . #0=#<identifier user#args>)
;; (#<identifier user#match> #0#
;; ((n)
;; (fact n 1))
;; ((n acc)
;; (if (zero? n)
;; acc
;; (fact (- n 1) (* n acc))))))
(fact 5)
;; 5
(define-overload greeting
(()(greeting "world"))
((name)(print #`"Hello, ,|name|!!")))
(greeting)
;; Hello, world!!
;; #<undef>
(greeting 'valvallow)
;; Hello, valvallow!!
;; #<undef>


プログラミングClojure

0 件のコメント:

コメントを投稿