ラベル syntax-rules の投稿を表示しています。 すべての投稿を表示
ラベル syntax-rules の投稿を表示しています。 すべての投稿を表示

2011/11/05

ておくlet

defmacro
(define-macro (teoku-let binds . body)
  `(let ,(map (lambda (ls)
               (cons (car ls)'("ておくれ"))) binds)
     ,@body))

(teoku-let ((wasao 68)
            (gentoo 'linux)
            (kumamoto "熊本"))
       (print wasao)
       (print gentoo)
       (print kumamoto))
;; ておくれ
;; ておくれ
;; ておくれ


syntax-rules
                                                                                                                     
(define-syntax teoku-let
  (syntax-rules ()
    ((_ () body ...)
     (let ()
       body ...))
    ((_ ((var val) x ...) body ...)
     (let ((var "ておくれ"))
       (teoku-let (x ...)
                  body ...)))))

(teoku-let ((wasao 68)
            (gentoo 'linux)
            (kumamoto "熊本"))
       (print wasao)
       (print gentoo)
       (print kumamoto))
;; ておくれ
;; ておくれ
;; ておくれ


プログラミングGauche

2011/02/12

プログラミング Gauche の object マクロと LOL の dlambda マクロ

プログラミング Gauche をパラパラ読んでたら、P.270 に object というマクロ(syntax-rules)が載ってたんですね。よく見たら、どこかで見たこのあるような形と機能だなーと。LOL(LET OVER LAMBDA Edition 1.0)の dlambda にそっくりなんですね。

dlambda はもともと Common Lisp の伝統的なマクロで書かれているので、見た目は object マクロとは似ていません。ですが、dlambda を scheme の衛生的マクロで書くとそっくりです。(似たような機能なのでそりゃそうなんですが)
以前書いた衛生的マクロ版 dlambda をちょっと書き直して再掲するとこんなの。
(define-syntax dlambda
  (syntax-rules (else)
    ((_ (msg (arg ...) body ...) ...)
     (^ (key . args)
        (case key
          ((msg)(apply (^ (arg ...)
                          body ...) args))
          ...
          (else key))
        ))))

使い方はこんな感じ。
(define counter
  (let1 count 0
    (dlambda
     (inc (:optional (n 1))(inc! count n))
     (dec (:optional (n 1))(dec! count n)))))

(counter 'inc)
;; -> 1
(counter 'inc)
;; -> 2
(counter 'dec 10)
;; -> -6


で、これが プログラミング Gauche に載ってる object マクロ。(P.270)
(define-syntax object
  (syntax-rules ()
    [(object (ivar ...) (method (arg ...) body ...) ...)
     (lambda (ivar ...)
       (lambda (message . args)
         (case message
           [(method) (apply (lambda (arg ...) body ...) args)]
           ...)))]
    ))

で、こんな感じでオブジェクトっぽいものを作るのに使える。
(define make-count
  (let1 count 0
    (object ()
            (inc (:optional (n 1))(inc! count n))
            (dec (:optional (n 1))(dec! count n)))))

(define counter (make-count))

(counter 'inc)
;; -> 1
(counter 'inc 10)
;; -> 11
(counter 'dec)
;; -> 10

メソッド名はキーワードでも良いかも。こんな風に。
(define make-count
  (let1 count 0
    (object (:optional step)
            (:inc (:optional (n step))(inc! count n))
            (:dec (:optional (n step))(dec! count n)))))

(define counter (make-count 3))

(counter :inc)
;; -> 3
(counter :inc 1)
;; -> 4
object マクロの方が初期値を取れる分良いかも。

プログラミング Gauche を読んでたら object マクロが目にとまりました -> LOL の dlambda に似てるなー -> ただそれだけです。

ついでに LOL に載ってる Common Lisp 版の dlambda 。
(defmacro! dlambda (&rest ds)
  `(lambda (&rest ,g!args)
     (case (car ,g!args)
       ,@(mapcar
           (lambda (d)
             `(,(if (eq t (car d))
                  t
                  (list (car d)))
               (apply (lambda ,@(cdr d))
                      ,(if (eq t (car d))
                         g!args
                         `(cdr ,g!args)))))
           ds))))

LOL の dlambda には defmacro! が必要なので、実際に使うには以下のようになる。伝統的マクロは確かに強力だけど、syntax-rules だと簡単に書けるものもあるので、syntax-rules も結構よくね?
(defun flatten (x)
  (labels ((rec (x acc)
             (cond ((null x) acc)
                   ((atom x) (cons x acc))
                   (t (rec
                        (car x)
                        (rec (cdr x) acc))))))
    (rec x nil)))

(defun mkstr (&rest args)
  (with-output-to-string (s)
    (dolist (a args) (princ a s))))

(defun symb (&rest args)
  (values (intern (apply #'mkstr args))))

(defun g!-symbol-p (s)
  (and (symbolp s)
       (> (length (symbol-name s)) 2)
       (string= (symbol-name s)
                "G!"
                :start1 0
                :end1 2)))

(defmacro defmacro/g! (name args &rest body)
  (let ((syms (remove-duplicates
                (remove-if-not #'g!-symbol-p
                               (flatten body)))))
    `(defmacro ,name ,args
       (let ,(mapcar
               (lambda (s)
                 `(,s (gensym ,(subseq
                                 (symbol-name s)
                                 2))))
               syms)
         ,@body))))


(defun o!-symbol-p (s)
  (and (symbolp s)
       (> (length (symbol-name s)) 2)
       (string= (symbol-name s)
                "O!"
                :start1 0
                :end1 2)))

(defun o!-symbol-to-g!-symbol (s)
  (symb "G!"
        (subseq (symbol-name s) 2)))

(defmacro defmacro! (name args &rest body)
  (let* ((os (remove-if-not #'o!-symbol-p args))
         (gs (mapcar #'o!-symbol-to-g!-symbol os)))
    `(defmacro/g! ,name ,args
       `(let ,(mapcar #'list (list ,@gs) (list ,@os))
          ,(progn ,@body)))))

(defmacro! dlambda (&rest ds)
  `(lambda (&rest ,g!args)
     (case (car ,g!args)
       ,@(mapcar
           (lambda (d)
             `(,(if (eq t (car d))
                  t
                  (list (car d)))
               (apply (lambda ,@(cdr d))
                      ,(if (eq t (car d))
                         g!args
                         `(cdr ,g!args)))))
           ds))))

LET OVER LAMBDA Edition 1.0

2010/11/27

syntax-rules: define-same-params

(define-same-params (idx edge)
  (index->x (remainder idx edge))
  (index->y (quotient idx edge))
  (index->xy (values (index->x idx edge)
                     (index->y idx edge)))
  (hline-indices (vector-iota edge (* (index->y idx edge) edge))))

小手先の誤魔化しのような気もしますが。

(define-syntax define-same-params
  (syntax-rules ()
    ((_ (params ...)(name body ...))
     (define (name params ...)
       body ...))
    ((_ (params ...)(name body ...) x ...)
     (begin
       (define-same-params (params ...)(name body ...))
       (define-same-params (params ...) x ...)
       (undefined)))))

追記

@valvallow R6RSならこれでいける(define-syntax define-same-params(syntax-rules()((_(p ...)(n b ...)...)(begin(define(n p ...)b ...)...)))) R5RSの微妙な不備
(define-syntax define-same-params
  (syntax-rules ()
    ((_ (p ...)(n b ...) ...)
     (begin
       (define (n p ...) b ...) ...))))

R6RS 。。

Scheme手習い

2010/09/10

PAIP: メモ化, memo, memoize, define-memo

メモ化。以前もいくつかいい加減な記事を書いています。。


メモ化については On Lisp や SICP(計算機プログラムの構造と解釈)なんかでも出てきますね。

今回はPAIP(実用 Common Lisp (IT Architects’Archive CLASSIC MODER))P.253 第3部 第9章 9.1 より。Common Lisp ではなく Gauche(Scheme)で書いてあるので、コードが多少違います。

以下コード。一つ目がmemo関数のプロトタイプで二つ目が本番 memo, meomize, define-memo, clear-memoize。都合により clear-memo も追加しています。

本番。



実用 Common Lisp (IT Architects’Archive CLASSIC MODER)

2010/09/09

syntax-rules, defmacro: define-cxr, define-cxr*

caaaar とか cadadadar などを定義するマクロ。LET OVER LAMBDA Edition 1.0 にもありました。

取りあえず書いてみました。
組み合わせて作っていく感じ。car + car で caar を作る、みたいな。
以下コード。


自分なりに考えてみたもの。もといし、効率も悪いけど意図通り動きます。
こちらは (define-cxr caar) とされたら a と d を car と cdr として手続きを組み立てる感じ。でも入力を制限していないので、my-caaar が caaar 相当の手続きに、ashitahadounaru? が caaadar 相当の手続きになります。。


追記

下のコード貼り間違えてました。修正。

追記2

map-accum でなくてもよかったですね。fold-right でよさそうです。
(define (cxr->ad-fun-lis sym)
  (fold-right (lambda (e acc)
                (let1 p (coalesce ((eq? e 'a) car)
                                  ((eq? e 'd) cdr))
                  (if p
                      (cons p acc)
                      acc))) '() (symbol->list sym)))

LET OVER LAMBDA Edition 1.0

syntax-rules: coalesce

これの関連で。。


こういうのが
(let1 e 'd
  (if (or (if (eq? e 'a)
              'car)
          (if (eq? e 'd)
              'cdr))
      'found
      'notfound))

こうなる
(let1 e 'd
  (if (coalesce ((eq? e 'a) 'car)
                ((eq? e 'd) 'cdr))
      'found
      'notfound))

cond みたい。inspired by SQL's coalesce.

プログラミングGauche

syntax-rules: if-true

(if (hoge? fuga) 'fuga #f) ってのをどうにかできないかなーと思うんですが良いアイディアがありません。マクロを書いてみましたがいまいち。。


追記

そうか and か!

プログラミングGauche

2010/09/08

Re: syntax-rules: define-overload (match-lambda*)

なるほど match-lambda* ですか!
実は match, match-lambda 辺りはよくわかってなかったので良い機会です。

書いてみました。以下コード。


というかもうマクロにする必要もなさそうですね。。match-lambda* 使えば良いですね・・・。

追記

case-lambda でよかったのかもしれない。。

プログラミングClojure

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

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

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

まずはマクロを書く前に展開イメージを

以下マクロ本体


プログラミングClojure

syntax-rules: dlambda

今日は、Twitter のタイムラインで LET OVER LAMBDA Edition 1.0 の話題が出ていました。
私も読みましたが、詳細はすでに記憶の彼方です。。再読したいところです。

記憶に残っている dlambda を scheme の syntax-rules で書いてみました。たぶん同じように動くと思います。

マクロを書く前に・・・
;; image
(define count-test
  (let ((count 0))
    (dlambda
     (:reset ()(set! count 0))
     (:inc (n)(inc! count n))
     (:dec (n)(dec! count n))
     (:bound (lo hi)
             (set! count (min hi (max lo count)))))))

;; expand image
(define count-test
  (let ((count 0))
    (lambda (key . args)
      (case key
        ((:reset)(apply (lambda ()
                          (set! count 0)) args))
        ((:inc)(apply (lambda (n)
                        (inc! count n)) args))
        ((:dec)(apply (lambda (n)
                        (dec! count n)) args))
        ((:bound)(apply (lambda (lo hi)
                          (set! count
                                (min hi (max lo count)))) args))
        (else key)))))

(count-test :reset)
;; 0
(count-test :inc 100)
;; 100
(count-test :inc 1)
;; 101
(count-test :inc 2)
;; 102
(count-test :bound -10 10)
;; 10
(count-test :reset)
;; 0
(count-test :inc 1)
;; 1

以下マクロのコード


LET OVER LAMBDA Edition 1.0

2010/08/27

syntax-rules: mreverse (マクロ reverse, 反転)

syntax-rules による reverse です。syntax-rules でマクロを書く時に良く使われるテクニック(?)。

helper を別途定義した場合。


プログラミングGauche

2010/08/26

syntax-rules: cut っぽい let

たまたま、試しにこういうアナフォリックマクロを書いていました。値を一時的に束縛したいけど、名前を付けたいわけではないことがよくある気がしたので。別に <> じゃなくて On Lisp の aif や aand みたいに it でも良いんですけども。

そこで、srfi-26 の cut っぽい let があったら便利そうだなぁ。。と思ったので、こちらも試しに書いてみました。みましたが・・・。(名前は、cut っぽい let -> cutlet -> cet と取りあえず)

ネストした時ダメですね。。こういう時はどう扱ったら良いんでしょうか。わかりません。以下のように少し書き足してもみましたが・・・。動いたとしても、ここまでするなら一番最初のアナフォリックマクロで良いかなーと思いました。


しかし、srfi のコードは美しいですねぇ。今回も大変勉強になりました。

追記

コメント欄が面白かったので。

プログラミングGauche

2010/08/13

syntax-rules:def-let* (slib)

syntax-rules でマクロを定義するマクロ def-let* がうまいこと動きませんでした。
これがその def-let*

そこで、また教えて頂きました!ありがとうございます!
@valvallow http://bit.ly/9Bvqri
@valvallow まず (use slib)(require 'repl)(require 'syntax-case) とします。 slib 環境内で評価したい式全体を quote して macro:eval 手続きに渡せばよいです。
@valvallow あるいは、 (repl:top-level macro:eval) とすると slib の repl が開始されるのでここに式を入力してもよいです。

動きましたー!以下そのコード。仕組みがよくわかってませんが。。

slib は今まで (use slib)(require 'trace) して trace, untrace くらいしか使ったことありませんでした。。slib って R5RS 準拠の pure scheme なライブラリなんですよね?srfi も良いですが、slib も読むと面白そうですね!

プログラミング言語SCHEME

2010/08/12

syntax-rules: fluid-let*

なんのことはありません。fluid-let を使って fluid-let* を書くわけですから、let を使って let* を書くのと変わりません。

;; fluid-let*

;; ;; example
;; (define-values (a b c)
;;   (values 1 2 3))

;; (define (a+b+c)
;;   (+ a b c))

;; (a+b+c)
;; ;; 6

;; (fluid-let* ((a 100)
;;              (b (* a 2))
;;              (c (+ a b)))
;;             (a+b+c))
;; ;; 600

つまり、以下のコードのようになります。

そうなると、こういうのが欲しくなります。(このコードは動かないと思います。)

syntax-rules でこういうの書くにはどうしたら良いんだろう。。たぶん、... のところがダメなんじゃないかとは思うのですが・・・。
プログラミングGauche

syntax-rules: fluid-let

fluid-let は dynamic scope をエミュレートするようなマクロです。

fluid-let は srfi にもあります。gauche などでは組み込みで用意されています。驚いたのは dynamic-wind が使われているところ。例えば body で脱出された場合などを考慮しているんですよね、きっと。なるほど~。

こういう動きです。
;; fluid-let

;; example
(define-values (a b)(values 1 2))

(define (a+b)
  (+ a b))

(a+b)
;; 3

(fluid-let ((a 100)(b 200))
  (a+b))
;; 300

;; fluid-let expand image
(let ((tempa a)(tempb b))
  (dynamic-wind
    (lambda ()
      (set! a 100)(set! b 200))
    (lambda ()
      (a+b))
    (lambda ()
      (set! a tempa)(set! b tempb))))
;; 300
(a+b)
;; 3
独習 Scheme 三週間 Teach Yourself Scheme in Fixnum Days に伝統的なマクロを用いた例が載っています。
fluid-let は syntax-rules では書けないかと思っていました。が、書けるようです。

当初、カンニングせずに自分で考えてみようと思いましたが、syntax-rules 内で一時的な変数を用意する方法がわかりませんでした。。そこで早速カンニングして写経してみました。

当然ですが、srfi のコードは美しいですねぇ・・・。
;; SRFI 15: Syntax for dynamic scoping - http://srfi.schemers.org/srfi-15/srfi-15.html
(define-syntax fluid-let
  (syntax-rules ()
    ((_ ((v1 e1) ...) b1 b2 ...)
     (fluid-let "temps" () ((v1 e1) ...) b1 b2 ...))
    ((_ "temps" (t ...) ((v1 e1) x ...) b1 b2 ...)
     (let ((temp e1))
       (fluid-let "temps" ((temp e1 v1) t ...) (x ...) b1 b2 ...)))
    ((_ "temps" ((t e v) ...) () b1 b2 ...)
     (let-syntax ((swap!
                   (syntax-rules ()
                     ((swap! a b)
                      (let ((tmp a))
                        (set! a b)
                        (set! b tmp))))))
       (dynamic-wind
         (lambda ()
           (swap! t v) ...)
         (lambda ()
           b1 b2 ...)
         (lambda ()
           (swap! t v) ...))))))

写経しつつ以下のように分解してみました。

上記の helper マクロを写経しているときに気づいたのですが、
(_ "temps" (t ...) ((v1 e1) x ...) b1 b2 ...)
     (let ((temp e1))
       (fluid-let "temps" ((temp e1 v1) t ...) (x ...) b1 b2 ...)))
この部分が素敵ですね!自分はこのテクニック(?)を知らなかったので、今まで以下のように書いていました。。

ところで、文字列を使ったフラグ(?)によって、以下のように一つのマクロにまとめることができるわけです。

でも、こういう風に書けたらもっとわかりやすい気がします。でも、確かダメなんですよね。。(... ...)とかドット対記法だと良いんでしたっけ?(その辺はまた今度・・・)



参考


;; SRFI 15: Syntax for dynamic scoping - http://srfi.schemers.org/srfi-15/srfi-15.html
(define-syntax fluid-let
  (syntax-rules ()
    ((_ ((v1 e1) ...) b1 b2 ...)
     (fluid-let "temps" () ((v1 e1) ...) b1 b2 ...))
    ((_ "temps" (t ...) ((v1 e1) x ...) b1 b2 ...)
     (let ((temp e1))
       (fluid-let "temps" ((temp e1 v1) t ...) (x ...) b1 b2 ...)))
    ((_ "temps" ((t e v) ...) () b1 b2 ...)
     (let-syntax ((swap!
                   (syntax-rules ()
                     ((swap! a b)
                      (let ((tmp a))
                        (set! a b)
                        (set! b tmp))))))
       (dynamic-wind
         (lambda ()
           (swap! t v) ...)
         (lambda ()
           b1 b2 ...)
         (lambda ()
           (swap! t v) ...))))))


;; Identifier Syntax - http://permalink.gmane.org/gmane.lisp.scheme.reports.wg1/148
(define-syntax fluid-let
  (syntax-rules ()
    ((fluid-let ("step") bind ((var val) . rest) body ...)
     (fluid-let ("step") ((var old new val) . bind) rest body ...))
    ((fluid-let ("step") ((var old new val) ...) () body ...)
     (let ((old var) ...
           (new val) ...)
       (dynamic-wind (lambda () (set! var new) ...)
                     (lambda () body ...)
                     (lambda () (set! var old) ...))))
    ((fluid-let ((var val) ...) body ...)
     (fluid-let ("step") () ((var val) ...) body ...))))


;; http://www-pu.informatik.uni-tuebingen.de/users/knauel/sw/fffi/easyffi.scm
(define-syntax fluid-let
  (syntax-rules ()
    ((fluid-let ((var1 expr1) (var2 expr2)) body ...)
     (let ((old-var1 var1)
           (old-var2 var2))
       (set! var1 expr1)
       (set! var2 expr2)
       (let ((res ((lambda () body ...))))
         (set! var1 old-var1)
         (set! var2 old-var2)
         res)))
    ((fluid-let ((var1 expr1)) body ...)
     (let ((old-var1 var1))
       (set! var1 expr1)
       (let ((res ((lambda () body ...))))
         (set! var1 old-var1)
         res)))))


;; Control Operations - http://www.scheme.com/tspl4/control.html
(define-syntax fluid-let
  (syntax-rules ()
    [(_ ((x e)) b1 b2 ...)
     (let ([y e])
       (let ([swap (lambda () (let ([t x]) (set! x y) (set! y t)))])
         (dynamic-wind swap (lambda () b1 b2 ...) swap)))]))

The Scheme Programming Language, 4th Edition

2010/08/08

PAIP 3.1 「let*式と等価なlambda式を示せ」をマクロで・・・

掲題の通りの問題です。今さら手書きするのもあれなので、マクロ書いてエキスパンドすれば良いんじゃね?と思ってマクロ書きましたが、思ったよりエキスパンドしてくれませんでした・・・。


実用 Common Lisp (IT Architects’Archive CLASSIC MODER)

2010/08/05

PAIP 2.2 でちょっとしたマクロ

PAIP(実用 Common Lisp (IT Architects’Archive CLASSIC MODER))の当該箇所の本題とは無関係なのですが、マクロを書いたので晒しておきます。

以下コード。一番上が書籍に載っているもの。2, 3番目がマクロ。

私は On LispLET OVER LAMBDA Edition 1.0 も読んだわけですが、"読んだ"だけで書けるようになったわけではありません。どうやら。書かないと書けるようにはならないでしょうね。両書籍も書きながら再読しないといけませんね。

ちなみに書籍は Common Lisp ですが、今のところ Scheme(Gauche)で書いています。
この分厚い書籍を携帯したり、電車の中で読むには勇気が要りますね。。今日から実行していますが。。

実用 Common Lisp (IT Architects’Archive CLASSIC MODER)

2010/06/29

syntax-rules: ext-let

CL とか Clojure の destructuring-bind があればそれで済みそうですが、Gauche にはどうやらなさげ?

syntax-rules だとこんな感じでしょうか。。

これだと、(ext-let (a b)(1)(print a)) などでも動きます。(var ...) と (val ...) が同じ長さでない場合エラーにしたいって時はどうしたら良いのでしょうか。

追記

なんとなく希望に近い動きしてるように見えますが・・・。うーん・・・頭痛くなりますね。補助マクロとして切り出せば少しはマシに見えるのかもしれませんね。


追記2

こんな感じ?ヘルパー。


プログラミングGauche

2010/06/24

syntax-rules: across (Clojure の .. ぽいもの)

プログラミングClojure P.62~ 「Clojure から Java を使う」より。
(.getLocation (.getCodeSource (.getProtectionDomain (.getClass '(1 2)))))

(.. '(1 2) getClass getProtectionDomain getCodeSource getLocation)
と書けますよーということのようです。

Java だと
'(1 2).getClass().getProtectionDomain().getCodeSourse().getLocation();
みたいなイメージで良いんでしょうかね。


scheme (gauche) の syntax-rules で書くと下記のような感じでしょうか。
良い名前が思いつきませんでした。当初は chain にしてましたが、なんとなく across に変えました。。

書いてみたコードは以下のようなもの。


追記

本家ソースはこれかな。

追記2

コメント欄で教えていただきました!名前が良いなぁ。
let1 要らないですね。。

プログラミングClojure