#:g1

fare-matcherの紹介

Posted 2014-02-27 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の59日目です。

fare-matcherとはなにか

 fare-matcherはfare氏作のパタンマッチのライブラリです。作者曰く、

THIS LIBRARY IS BEING DEPRECATED IN FAVOR OF OPTIMA. PLEASE USE OPTIMA INSTEAD.

とのことで、現在はOptima利用推奨のようです。

パッケージ情報

パッケージ名fare-matcher
Quicklisp×
CLiKihttp://cliki.net/fare-matcher

インストール方法

(asdf:load-system :fare-matcher)

試してみる

 作者がOptimaを使えというだけあって、Quicklispからも外れてしまったようです。ちょっと前にQuicklispでインストールすると、length=n-pが無いので動かなかったりしました。(ASDFのuiop/utility:length=n-pを持ってくればOK)

 このままさよならなのかfare-matcherということで記念にベンチを取ってみました。競争相手は高速なtoadstoolです。

(defun h-toad (op a b)
  (toadstool:toad-case op a b
     '+ M N -> (when (and (numberp M) (numberp N))) (+ M N) 
     '+ 0 F -> F
     '+ F 0 -> F
     '+ A (list '+ B C) toadstool:-> (h '+ (h '+ A B) C)
     '* M N -> (when (and (numberp M) (numberp N))) (* M N)
     '* 0 F -> 0
     '* F 0 -> 0
     '* F 1 -> F
     '* 1 F -> F
     '* A (list '* B C) toadstool:-> (h '* (h '* A B) C)
     Op A B -> (list Op A B)))

(defun harropify-toad (x) (toadstool:toad-case x (list Op A B) toadstool:-> (h-toad Op (harropify-toad A) (harropify-toad B)) A toadstool:-> A))

(defun h-fm (op a b) (fare-matcher:match (list op a b) ((list '+ A (list '+ B C)) (h-fm '+ (h-fm '+ A B) C)) ((list '+ (and M (fare-matcher:of-type integer)) (and N (fare-matcher:of-type integer))) (+ M N) ) ((list '+ 0 F) F) ((list '+ F 0) F) ((list '* (and M (fare-matcher:of-type integer)) (and N (fare-matcher:of-type integer))) (* M N)) ((list '* 0 F) 0) ((list '* F 0) 0) ((list '* F 1) F) ((list '* 1 F) F) ((list '* A (list '* B C)) (h-fm '* (h-fm '* A B) C)) ((list Op A B) (list Op A B))))

(defun harropify-fm (x) (fare-matcher:match x ((list Op A B) (h-fm Op (harropify-fm A) (harropify-fm B))) (A A)))

(harropify-toad '(* x (+ (+ (* 12 0) (+ 23 8)) y))) ;=> (* X (+ 31 Y))

(harropify-fm '(* x (+ (+ (* 12 0) (+ 23 8)) y))) ;=> (* X (+ 31 Y))

(dotimes (i (expt 10 7)) (harropify-toad '(* x (+ (+ (* 12 0) (+ 23 8)) y)))) ;=> NIL #|------------------------------------------------------------| Evaluation took: 3.053 seconds of real time 3.044189 seconds of total run time (3.032189 user, 0.012000 system) [ Run times consist of 0.092 seconds GC time, and 2.953 seconds non-GC time. ] 99.71% CPU 7,307,637,444 processor cycles 960,016,720 bytes consed

Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz |------------------------------------------------------------|#

(dotimes (i (expt 10 7)) (harropify-fm '(* x (+ (+ (* 12 0) (+ 23 8)) y)))) ;=> NIL #|------------------------------------------------------------| Evaluation took: 49.109 seconds of real time 48.659041 seconds of total run time (48.379023 user, 0.280018 system) [ Run times consist of 5.357 seconds GC time, and 43.303 seconds non-GC time. ] 99.08% CPU 117,566,159,256 processor cycles 34,240,014,864 bytes consed

Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz |------------------------------------------------------------|#

toadstoolが16倍位速いですね。

fare-matcherの紹介/関連記事等

 すっかり忘れてましたが、5年前にも紹介記事を書いていたことがfare-matcher関係のものをググっていて発覚しました。

まとめ

 今回は、fare-matcherを紹介してみました。
fare-matcherを使おうという人は、作者の意向を汲んでOptimaを使いましょう。

clfswmの紹介

Posted 2014-02-26 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の58日目です。

clfswmとはなにか

 clfswmはStumpWMと同じくCommon Lisp製で全画面型(タイル型)のウィンドウマネージャーです。

パッケージ情報

パッケージ名clfswm
Quicklisp
プロジェクトサイトCLFSWM
CLiKihttp://cliki.net/clfswm
Quickdocshttp://quickdocs.org/clfswm

インストール方法

(ql:quickload :clfswm)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 プロジェクトのページに多数の動画がありますので、どんな使い勝手かは動画をみると良く分かるかなと思います。
StumpWMはGNU Screenっぽく画面が並置される感じですが、clfswmは、階層分けで管理する傾向があるようです。
また、MacOSXのexposeのような機能がなかなか便利かもしれません。 clfswm-expose

 どんなものかちょっと試してみましたが、自分は、StumpWM風にカスタマイズして終わりました。

(in-package :clfswm)

(define-main-key (#\t :control) 'second-key-mode)

(progn (defun $start-emacs () "start emacs" (setf *second-mode-leave-function* (lambda () (run-or-raise (lambda (win) (string-equal "emacs" (xlib:get-wm-class win))) (lambda () (do-shell "cd $HOME && exec emacsclient -c"))))) (leave-second-mode))

(define-second-key ("e") '$start-emacs) (define-second-key ("e" :control) '$start-emacs))

(progn (defun $start-firefox () "start firefox" (setf *second-mode-leave-function* (lambda () (run-or-raise (lambda (win) (string-equal "firefox" (nth-value 1 (xlib:get-wm-class win)))) (lambda () (do-shell "cd $HOME && exec firefox"))))) (leave-second-mode))

(define-second-key ("f") '$start-firefox) (define-second-key ("f" :control) '$start-firefox))

(defun expose-windows-mode/second-mode () (setf *second-mode-leave-function* #'expose-windows-mode) (leave-second-mode))

(define-second-key ("e" :mod-1) 'expose-windows-mode/second-mode)

(progn (defun $start-screen () "start screen" (setf *second-mode-leave-function* (lambda () (run-or-raise (lambda (win) (string-equal "gnome-terminal-server" (xlib:get-wm-class win))) (lambda () (do-execute "/usr/bin/gnome-terminal" ()))))) (leave-second-mode)) (define-second-key ("c") '$start-screen) (define-second-key ("c" :control) '$start-screen))

 軸になるsecond-keyをStumpWM風にControl-tにしたり、お馴染のrun-or-raiseでemacsや、firefoxを起動できるようにしたり、です。
exposeは、C-t M-eで起動するようにしてみました。

 上記設定コードを眺めれば分かるかと思いますが、Common Lispそのままの感じで設定を書きます。StumpWMのように設定用のちょっとしたDSLが構築されている感じでもないようです。
ちなみに、例では読み込んでいませんが、contribディレクトリに色々なツールがあるようなので試してみるのも良いかなと思います。

まとめ

 今回は、clfswmを紹介してみました。
StumpWMの改良型かと思いましたが、割と違った所が多いようです。
ちなみに、second-keyに割り当てたキーをアプリに送る方法が分からず(StumpWMでいう、Control-T Tのようなもの)、firefoxのタブを開いたり閉じたりで困ったりです。

stumpwmの紹介

Posted 2014-02-25 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の57日目です。

stumpwmとはなにか

 stumpwmは、Common Lisp製のウィンドウマネージャーです。

パッケージ情報

パッケージ名stumpwm
Quicklisp
プロジェクトサイトThe Stump Window Manager
CLiKihttp://cliki.net/stumpwm

インストール方法

(ql:quickload :stumpwm)

でロード可能です。
インストールスクリプトから導入するのもありですが、Common Lispを扱える人なら、Quicklispでロードしてからイメージをダンプして実行ファイルを作成するという手もあります。

試してみる

 どんな関数があるかは、Quickdocsで確認できます。ライブラリではありませんが。

 StumpWMがどんなものかは、少々古いですが、この動画で知ることができます。

 なんといってもキー操作一発で欲しいものにフォーカスできるということが最大の長所ではないでしょうか。
いや忘れていましたが、Common Lispでカスタマイズできることも長所ですね。

stumpwmの紹介/関連記事等

まとめ

 今回は、stumpwmを紹介してみました。
自分は、かれこれ6、7年StumpWMを愛用しています。StumpWMよりもっと良いものがあると思いますし、StumpWMに不満がない訳でもないですが、自分的な局所最適解を得てしまったというところで、最早StumpWMから抜け出せません。

coolの紹介

Posted 2014-02-24 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の56日目です。

coolとはなにか

 coolは、Common Lispにオブジェクト指向を取り入れる際にCommonLoopsや、Flavors、Object LISPと共に検討されたオブジェクト指向システムの一つである、CommonObjectsの実装一つです。

パッケージ情報

パッケージ名cool
Quicklisp
参考サイトPackage: lang/lisp/oop/non_clos/cool/

インストール方法

 上記のCMUのサイトからダウンロードしてロードします。もしくは、SBCLで動くようにしてASDFに対応したものがありますので、興味のある方は試してみて下さい

Quicklispのlocal-project以下に展開すれば、

(ql:quickload :cool)

でもロード可能です。

試してみる

 Sheepleの時と同様に、BankAccountというのを書いてみます。


(define-type bank-account
  (:var dollars (:init 0) :initable :gettable :settable))

(define-method (bank-account :deposit) (n) (incf dollars n))

(define-method (bank-account :withdraw) (n) (setf dollars (max 0 (- dollars n))))

(defparameter *my-account* (make-instance 'bank-account :dollars 200)) ;=> *MY-ACCOUNT*

(=> *my-account* :dollars) ;=> 200

(=> *my-account* :deposit 50) ;=> 250

(=> *my-account* :withdraw 100) ;=> 150

(=> *my-account* :withdraw 200) ;=> 0

(=> *my-account* :dollars) ;=> 0

 スロットの初期化可能、参照可能、書き込み可能は、:initable、:gettable、:settableで指定するところが、Flavorsと似ています。
ここでCLOSに馴れた人からすると目に付くのは、メソッド本体の中で変数がローカル変数として参照できることでしょうか。
=> は 所謂sendです。CLOSのように多重メソッドではありません。

 そして、このbank-accountを継承した、stock-account。
stock-accountのdollarsがスロットの値とは独立に(同じ結果を)計算するのがどうも気持ち悪いのですが、オーバーロードできることを示しているようでもあるらしいので、そのまま書きます。


(define-type stock-account 
  (:inherit-from bank-account)
  (:var num-shares (:init 0))
  (:var price-per-share (:init 30))
  :all-initable
  :all-gettable)

(define-method (stock-account :set-dollars) (n) (setf num-shares (/ n price-per-share)) (call-method (bank-account :set-dollars) n))

(define-method (stock-account :dollars) () (* num-shares price-per-share))

(define-method (stock-account :deposit) (n) (=> self :set-dollars (+ n (call-method (bank-account :dollars)))))

(define-method (stock-account :withdraw) (n) (=> self :set-dollars (+ (- n) (call-method (bank-account :dollars)))))

(defparameter *my-stock* (make-instance 'stock-account :num-shares 10)) ;=> *MY-STOCK*

(=> *my-stock* :dollars) ;=> 300

(=> *my-stock* :set-dollars 600) ;=> 600

(=> *my-stock* :num-shares) ;=> 20

(=> *my-stock* :deposit 60) ;=> 660

(=> *my-stock* :num-shares) ;=> 22

(=> *my-stock* :withdraw 120) ;=> 540 (=> *my-stock* :num-shares) ;=> 18

 CommonObjectsに特徴的なのは、親のスロットをマージしない点です。
同名スロットがぶつかることがないのでその辺りは気にしなくても良いのですが、参照する場合は、継承関係に関係なくcall-methodで参照する必要があります。つまり継承関係は関係なく公開されたスロットにしか外部からはアクセスできない、ということになります。
とはいえ、インスタンスのスロットは同じオブジェクト内に同居しているので、なんとも不思議な感覚です。
:all-gettable等の:allはスロット全部に対しての指定。これもFlavorsに似ています。

 スロットとは対照的にメソッドはデフォルトで親のものを全部引き継ぎます。
引き継ぎたくない場合は、(:method)節で指定しますが、ホワイトリストなので一つのメソッドだけ引き継ぎたくないという場合面倒な感じです。
その他、CommonObjectsは多重継承だったりしますが、多重継承特有の問題については、まだ調べてません…。

coolの紹介/関連記事等

まとめ

 今回は、coolを紹介してみました。
coolはかなり初期の1986年位のPortable CommonLoopsの上に実装されていますが、そのうちCLOS MOPで書き直してみたいところです。

trivial-tcoの紹介

Posted 2014-02-23 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の55日目です。

trivial-tcoとはなにか

 trivial-tcoはRalph Möritz氏作の末尾再帰の最適化が効くようにオプションを設定したフォームを提供するものです。

パッケージ情報

パッケージ名trivial-tco
Quicklisp
Quickdocshttp://quickdocs.org/trivial-tco

インストール方法

(ql:quickload :trivial-tco)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。
といってもwith-tail-call-optimization1つだけですが。

 Schemeの心で末尾再帰で書いてもスタックが溢れてしまうのがCommon Lisp。
大抵の処理系は末尾呼び出しを最適化しますが、Schemeより最適化を邪魔するもの(スペシャル変数など)が多いので扱い難いものとなります。
とりあえず、そういう邪魔になるものは考えなければ、コンパイラは最適化オプションによって呼び出しをジャンプに変換します。
trivial-tcoは処理系ごとのオプションの違いを吸収するマクロで、定義もこれだけです。

(defmacro with-tail-call-optimization ((&optional treat-warnings-as-errors) &body body)
  #+ (or sbcl allegro)
  `(let ()
     (declare (optimize (speed 3)))
     ,@body)
  #+ (or lispworks ccl)
  `(let ()
     (declare (optimize (debug 0)))
     ,@body)
  #- (or cmu sbcl ccl lispworks allegro)
  (let ((msg "Proper tail-call optimization is not available."))
    (if treat-warnings-as-errors
        (error msg)
        (progn 
          (warn msg)
          `(progn
             ,@body)))))

 使い方もシンプルで、とりあえず最適化されて欲しいフォームを囲むだけです。
以前紹介した、そのままではスタックが溢れてしまうletrecが丁度良いので例として組み合せてみます。
まずは、素の再帰でのループ

(defun foo ()
  (letrec:letrec ((iter (lambda (n)
                          (if (zerop n)
                              :done
                              (iter (1- n))))))
    (iter 1000000)))

(foo) ;!!>Control stack exhausted (no more space for function call frames). ;!!>This is probably due to heavily nested or infinitely recursive function ;!!>calls, or a tail call that SBCL cannot or has not optimized away.

スタックが溢れます。
次にwith-tail-call-optimization付き

(defun foo ()
  (tco:with-tail-call-optimization ()
    (letrec:letrec ((iter (lambda (n)
                            (if (zerop n)
                                :done
                                (iter (1- n))))))
      (iter 1000000))))

(foo) ;=> :DONE

無事最適化されました。
なかなか良いねと思ったのですが、作者はClozure CLでしかテストしてないよとのことで、LispWorks/Allegro CLで試してみたところ設定オプションが違うようで最適化が効かないようです。SBCLでは最適化は効きましたが、今後に期待しましょう。

末尾呼び出しの最適化についての関連記事等

まとめ

 今回は、trivial-tcoを紹介してみました。
TCOのまとめを書いてる人が作成したのかと思い込んでいましたが、まとめ記事は、Marc Simpsonという方が書いているようなので別人でした。
今後trivial-tcoにもこのまとめの内容が盛り込まれると嬉しいですね。それとletで囲むんじゃなくて、locallyになると嬉しい。

(net oauth)の紹介

Posted 2014-02-22 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の54日目です。

(net oauth)とはなにか

 (net oauth)は R7RS準拠のsagittarius schemeのOAuthのライブラリです

パッケージ情報

パッケージ名(net oauth)
マニュアルSagittariusUsers'Reference

インストール方法

標準添付のライブラリなので
(import (net oauth))
するだけです。

試してみる

 マニュアルに詳しいコード例も載っているので、そちらを参考にすれば簡単に使えると思いますが、Twitterにポストする例を書いてみます。

(import (rnrs) (net oauth) (sagittarius io) (json) (srfi :1))

#|| ;; .twitter-oauth.lisp の形式 ((consumer-key "......") (consumer-secret ".....") (access-key "...") (access-secret "...")) ||#

(define .twitter-oauth.lisp ".twitter-oauth.lisp")

(define (readfile1 file) (call-with-input-file file read))

(define access-token (let ((token (readfile1 .twitter-oauth.lisp))) (define (<o key) (second (assoc key token))) (lambda () (make-access-token :key (<o 'access-key) :secret (<o 'access-secret) :consumer (make-consumer-token :key (<o 'consumer-key) :secret (<o 'consumer-secret))))))

(define (twclient :key (get "statuses/home_timeline") (? '() ) (post #f)) (let ((meth (if post 'POST 'GET)) (rsrc (or post get))) (reverse! (call-with-input-string (access-protected-resource (string-append "https://api.twitter.com/1.1/" rsrc ".json") (access-token) :request-method meth :user-parameters ?) json-read))))

;;;; (define (update mesg :key (re #f)) (twclient :post "statuses/update" :? `(("status" ,mesg) ,@(if re `(("in_reply_to_status_id" (number->string ,re))) '() ))))

;; (define tw update)

(tw "食べすぎた")
;=> [jsonの塊......

 どうも認証トークンをファイルから読んでみたりと、いらないものをごちゃごちゃ書いてしまいましたが、access-protected-resourceが肝で、これにURLとパラメータをtwitterのAPIの通り渡してやればOKです。
cl-oauthを移植したものとのことでcl-oauthを利用したことがある人には、そのままの感覚で利用できると思います。

まとめ

 今回は、(net oauth)を紹介してみました。
sagittariusは実用指向とのことで便利なライブラリも標準で色々添付されてきます。
一度眺めてみては如何でしょうか。

cl-m4の紹介

Posted 2014-02-21 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の53日目です。

cl-m4とはなにか

 cl-m4は、m4のCommon Lisp実装です。

パッケージ情報

パッケージ名cl-m4
Quicklisp
CLiKihttp://cliki.net/cl-m4
Quickdocshttp://quickdocs.org/cl-m4

インストール方法

(ql:quickload :cl-m4)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。といっても外部シンボルはprocess-m4のみですが。

 GNU M4互換を目指して一から作られたM4のCommon Lisp実装だそうですが、M4からASTを取得する方法がないのと、GNU M4でもマクロの評価/展開をフックする十分な手段が提供されていないことがCommon Lisp版開発の動機なのだそうです。
使い方はシンプルにprocess-m4に入出力のストリームを与えます。

(with-input-from-string (in "
  define(`ALPHA', `abcdefghijklmnopqrstuvwxyz')
  define(`ROT13', `nopqrstuvwxyzabcdefghijklm')

translit(`abc ebg13', ALPHA, ROT13) ") (with-output-to-string (out) (cl-m4:process-m4 in out))) ;=> " ; ; ; ; nop rot13 ; "

 わざわざm4をCommon Lisp上に実装するからには、全てをCommon Lisp上で実現する方向なのかと思いましたが、regexはlibcのものをcffi経由で利用するようです。
もしかしたら、そのうちregexもCommon Lisp実装になるのかもしれません。

まとめ

 今回は、cl-m4を紹介してみました。
defmacroからm4を呼び出してマクロを定義してみようかなと思いましたが、いまいち面白いものが作れず。
それはともかく世の中には色々なものがあるものです。

policy-condの紹介

Posted 2014-02-20 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の52日目です。

policy-condとはなにか

 policy-condはRobert Smith氏作の最適化ポリシーに応じて評価する式を切り換えるユーティリティです。

パッケージ情報

パッケージ名policy-cond
Quicklisp
Quickdocshttp://quickdocs.org/policy-cond

インストール方法

(ql:quickload :policy-cond)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 ソースを眺める限りではpolicy-condのpolicyとは、最適化ポリシー/宣言のことのようです。
使い方は非常に簡単で、policy-condの中では、optimizeで利用できるポリシーが変数名となっていて、値を参照できるので

(locally
  (declare (optimize (speed 3)))
  (policy-cond:policy-cond 
    ((= 3 speed) :speed-3)))
;=>  :SPEED!

(locally (declare (optimize (speed 3) (safety 0))) (defun fib (n) (policy-cond:policy-cond ((= 3 speed) (locally (declare (fixnum n)) (if (< n 2) n (the fixnum (+ (the fixnum (fib (1- n))) (the fixnum (fib (- n 2)))))))) (t (if (< n 2) n (+ (fib (1- n)) (fib (- n 2))))))))

(fib 30) ;=> 832040

のように分岐することが可能になっています。
上記の例はあまり良い例ではありませんが、最適化オプションを付ける前の式は別途取って置いて必要に応じて切り換えたりする場合には重宝するかもしれません。

 ポリシーの取得には、CLtL2で提案されていたdeclaration-informationを利用しています。
残念ながらdeclaration-informationもANSI CLには含まれていませんが、大抵の処理系でサポートしているようではあります。

 利用上の注意としては、コンパイラへの最適化宣言なので実行時ではなくコンパイル時に分岐が発生します。
また、マクロ展開では、展開器はローカルな最適化宣言まで面倒を見てくれない(と思う)ので、大域の宣言の内容での展開となり、結果としてコンパイラの動作とは違ってくることがあるかなと思います。

まとめ

 今回は、policy-condを紹介してみました。
policy-condという名前をみつけて面白そうだなと思って作者を確認してみたら、またRobert Smith氏でした。

kmrclの紹介

Posted 2014-02-19 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の51日目です。

kmrclとはなにか

 kmrclはKevin Rosenberg(kmr)氏のユーティリティ集です。

パッケージ情報

パッケージ名kmrcl
Quicklisp
CLiKihttp://cliki.net/kmrcl
Quickdocshttp://quickdocs.org/kmrcl

インストール方法

(ql:quickload :kmrcl)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 結構規模の大きなユーティリティ集ですが、大まかには、

  • 入出力
  • ストリーム I/O関係
  • ユーティリティマクロ
  • ソケット
  • MOP
  • リスト処理関数関数
  • 文字列処理関数
  • REPL/リスナー
  • OSインターフェイス
  • 処理系依存機能のポータブルレイヤー

位に分けられます。

 このブログでは、2009年から「KMRCLを眺める」というお題でkmrclの関数を眺めるシリーズを書いていて現在236エントリーありますが、暇潰しにどんな関数があるか等眺めてもらえると嬉しいです。
関数のドキュメントストリング以上の情報があるかといえば若干疑問ではありますが…。
ちなみに、個人的に良く使う関数は、kl:read-file-to-string、kl:with-each-file-lineあたり。(kmrclのニックネームはkl)

まとめ

 今回は、kmrclを紹介してみました。
Common Lispで速いけどベタなコードの書き方についてはkmrclのユーティリティを眺めていて知ったことが自分は割と多いかもしれません。
「KMRCLを眺める」シリーズもあと少しなので好い加減完結させたいところです。

mbeの紹介

Posted 2014-02-18 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の50日目です。

mbeとはなにか

 mbeはDorai Sitaram氏によるEugene Kohlbecker氏のMacro by Example(R4RSの付録のマクロ)の実装です。

パッケージ情報

パッケージ名mbe
プロジェクトサイト Macro by Example

インストール方法

 Common Lispの場合は、サイトから拾ってきて適当にロードします。
ちなみに、私がASDFでロードできるようにしたものもありますので興味のある方はどうぞ

 Schemeの場合は、slibに含まれているので、slibをロードできる処理系では、slibでの導入が簡単かと思います。

Gaucheの場合

(use slib)
(require 'common-list-functions)
(require 'rev4-optional-procedures)
(require 'defmacroexpand)
(require 'macro-by-example)

試してみる

 Common Lisp版とScheme版がありますが、Common Lispをメインに紹介してみます。
とはいえ、プロジェクトのサイトに丁寧に解説されているので、利用にあたってざっと簡単なところを紹介

Common Lisp版は衛生マクロでない

 Common Lisp版は、100行程度のシンプルな実装ですが、変数の衝突を自動で回避してくれる仕組みは入っていません。
withという変数のリネーム用の構文があるので、これで手動gensymして回避します。

Scheme版
(define-syntax kandi
  (syntax-rules ()
    ((kandi arg ...)
     (let ((a 1) (b 2) (c 3))
       (list a b c arg ...)))))

(kandi 1 2 3) ;==> (let ((slib:G290 1) (slib:G291 2) (slib:G292 3)) (list slib:G290 slib:G291 slib:G292 1 2 3))

Common Lisp版
(define-syntax kandi
  (syntax-rules ()
    ((kandi arg ***)
     (with ((a (gensym))
            (b (gensym))
            (c (gensym)))
       (let ((a 1) (b 2) (c 3))
         (list a b c arg ***))))))

(kandi 1 2 3) ;==> (LET ((#:G3504 1) (#:G3505 2) (#:G3506 3)) (LIST #:G3504 #:G3505 #:G3506 1 2 3))

バグ?: Common Lisp版

 ちなみにCommon Lispのオリジナルの実装では、ネストしたパタンの展開でバグがあるようですので、修正githubに置いてあるものは修正してあります。
このパタンはScheme版ではちゃんと展開するようです。

(define-syntax badios
  (syntax-rules ()
    ((badios (a ***) (b ***))
     (list (b a) ***))))

(badios (1 1 1 1) (2 2 2 2)) ;==> (LIST (B 1) (B 1) (B 1) (B 1)) ;??? ;==> (LIST (2 1) (2 1) (2 1) (2 1))

Common Lisp/Scheme版共通
(define-syntax baz2
  (syntax-rules ()
    ((foo (a ... b) ...)
     (append (list a ... '(b)) ...))))

のようなパタンはエラーになるようです。
他にもつつけば色々ある気はしますが、実用上ではそんなに困ることもないかなと思います。

mbeの紹介/関連記事等

まとめ

 今回は、mbeを紹介してみました。
自分は、SRFIのCommon Lispへの移植でdefine-syntaxを利用しているものは、mbeを使って移植してみていましたが、大き目の込み入ったマクロでもほぼそのまま動かすことができました。
衛生的ではないもののSchemeからCommon Lispにコードを移植する際には、かなり便利ですのでお勧めです。

Older entries (1633 remaining)