#:g1

LISP日記 2012-03-01

Posted 2012-03-01 04:20:00 GMT

分かったこと

  • named letだと、末尾再帰だと思ってしまうが、そうでないパターンに遭遇した。しかも 名前はloop。末尾再帰でなく使われる可能性もあると考えると、安直にマクロでgotoに直すという訳にもいかない。結局labelsでの置き換え位が安全かなというところ
  • 局所的な再帰の命名で、線形のものは loop 、そうでないものは rec という風にしているのも見たことがある。こういう使い分けは良いかもしれない
  • CCL 1.8からcase等が最適化されるようになったけれど、以前に調べた時にAllegro CLもテーブルを作っているらしいことが分かっていた。分岐の為のテーブルでもないようだし、なんのためのテーブルなんだろうと思っていたが、良く読んだらやっぱり分岐のためのテーブルだった。発動する条件はいまいち分かっていない

やってみたこと

  • 特になし

やりたいこと

  • 特になし

思ったこと

  • seriesの謎のエラーは、シンボルのプロパティリストが予期したものと違う場合に発生することが多い気がするけど、まだ良く分からない。コードウォークで失敗してる気がする

CLでsrfi-22

Posted 2012-02-28 20:02:00 GMT

CLでsrfi、今回は、srfi-22の「Running Scheme Scripts on Unix」です。
シェルスクリプトとして使われた場合の取り決め的なsrfiです。
とり決めの内容としては、#!の扱い、スクリプト内でmain関数を定義を書くと、それが実行されること、引数は文字列のリストで渡ってくること、位です。

動作

#!/usr/bin/env sbcl-srfi-22

(defun display-file (filename) (with-open-file (in filename) (loop :for line := (read-line in nil nil) :while line :do (write-line line))))

(defun main (arguments) (dolist (f (cdr arguments)) (display-file f)) 0)

のようなものを書いて、シェルから実行します。

移植について

どういう風にするか迷いましたが、バイナリであることも前提のようなので、とりあえずSBCL限定で、srfi-22の仕組みが組込まれた実行可能ファイルを作るライブラリということにしました。
$ sbcl --load srfi-22.lisp
とすると、ディレクトリに、sbcl-srfi-22という実行ファイルができるので、これをパスの通った場所に設置します。
実行時のパッケージは、srfi-22。cl、cl-userがuse-packageされています。
また、#!の処理をするために、#!のリーダーマクロを定義しているので、#!がリーダーマクロとして定義されたスクリプトは上手く動かないことになります。しかし、まあ、OKではないかなということで。 ■

sbclのsource-transformの結果を簡単に表示させたい

Posted 2012-02-26 20:02:00 GMT

sbclでは、通常の関数も最適化の変形がされてからコンパイルされますが、その最適化のための変形は、SB-C:DEFINE-SOURCE-TRANSFORM で定義できたりします。
このsource-transformが施される関数をmacroexpandのようにslimeで簡単に見れたら良いなと思い作ってみました。特に何かに必要な訳ではないのですが…。
CL側ではこんな感じ

(defun source-transform (form &optional (env (sb-kernel:make-null-lexenv)))
  (if (and (consp form)
           (symbolp (car form))
           (not (special-operator-p (car form))) )
      (let ((sb-c::*lexenv* env))
        (or (and (fboundp (car form))
                 (multiple-value-bind (fun win)
                                      (sb-int:info :function :source-transform (car form))
                   (and win (funcall fun form))))
            (values form T) ))
      (values form T) ))
slime側ではこんな感じ
;; el: slime
(defun slime-source-transform ()
  (interactive)
  (slime-eval-macroexpand 'g000001::source-transform-string))

(define-key slime-mode-map [(control ?c) (meta ?t)] 'slime-source-transform)

動作

普通に関数の実行
(mapcar (^x x) '(1 2 3 4))
;=>  (1 2 3 4)
source-transformした結果と、展開の実行結果
(LET ((G1936
       (SB-KERNEL:%COERCE-CALLABLE-TO-FUN
        (^X
          X))))
  (LET ((G1938 (LIST NIL)))
    (SB-INT:DO-ANONYMOUS ((G1937 G1938) (G1935 '(1 2 3 4) (CDR G1935)))
                         ((OR (ENDP G1935))
                          (SB-EXT:TRULY-THE LIST (CDR G1938)))
                         (RPLACD G1937
                                 (SETQ G1937
                                         (LIST
                                          (SB-C::%FUNCALL G1936
                                                          (CAR G1935))))))))
;=>  (1 2 3 4)
さらにmacroexpand-allしたものと、展開の実行結果
(LET ((G1936 (SB-KERNEL:%COERCE-CALLABLE-TO-FUN (LAMBDA (X) X))))
  (LET ((G1938 (LIST NIL)))
    (BLOCK G1942
      (LET ((G1937 G1938) (G1935 '(1 2 3 4)))
        (TAGBODY
          (GO G1944)
         G1943
          (TAGBODY
            (RPLACD G1937
                    (SETQ G1937 (LIST (SB-C::%FUNCALL G1936 (CAR G1935))))))
          (LET* ()
            (MULTIPLE-VALUE-BIND (NEW1945)
                (CDR G1935)
              (PROGN (SETQ G1935 NEW1945) NIL)))
         G1944
          (IF (THE T (ENDP G1935))
              NIL
              (PROGN (GO G1943)))
          (RETURN-FROM G1942 (PROGN
                               (SB-EXT:TRULY-THE LIST (CDR G1938)))))))))
;=>  (1 2 3 4)
別に役に立ちませんが、眺めてると面白いです。ちなみにコンパイルした結果は上記3つとも全部同じになります。

LISP日記 2012-02-27

Posted 2012-02-26 19:02:00 GMT

分かったこと

  • 特になし

やってみたこと

  • 特になし

やりたいこと

  • 特になし

思ったこと

  • ASDFでは、なんだかんだで、パッケージの定義は、独立したファイルに纒めた方がデバッグ等が楽な気がする。評価の順番も指定できるし
  • multiple-value-listのようなことをstringでやろうとすると、それに相当するようなのってないなと思った。思っただけで、別に必要ではない。
    (multiple-value-call #'vector (values 1 2 3 4))
    ;=>  #(1 2 3 4)

    (multiple-value-call #'list (values 1 2 3 4)) ;=> (1 2 3 4)

    (multiple-value-call #'format nil "~@{~A~}" (values #a #b #c #d)) ;=> "abcd"


LISP日記 2012-02-27

Posted 2012-02-26 14:59:00 GMT

分かったこと

  • 特になし

やりたいこと

  • 特になし

みつけたもの

  • マクロの展開形をそのまま評価したいことが割と良くある。macroexpandしただけだとuninterned symbolが評価できなかったりするので、これをどうにかしないといけない。このためにユーティリティを作ったりしていたが、ふと、*print-gensym*をnilにすれば良いんじゃないかと思って試してみたら、なんのことはない、これでOKだった…。*print-gensym*の存在意義が分からなかったが、もしかして、こういう場合に使うのだろうか。
  • slimeと連携させたいのでswank.lispを眺めてみると、swank::*macroexpand-printer-bindings*で表示のオプションを制御するらしかったので、ここをカスタマイズすれば良いらしい。リードテーブルの面倒もみてくれるのが便利
    (let ((swank::*macroexpand-printer-bindings*
           (cons '(*print-gensym*)
                 swank::*macroexpand-printer-bindings*) ))
      (swank:swank-macroexpand-all "(collect (#M1+ (scan '(1 2 3 4))))"))
    ;=>  "(LET* (ELEMENTS-2004
    ;           (LISTPTR-2005 '(1 2 3 4))
    ;           ITEMS-2002
    ;           (LASTCONS-1998 (LIST NIL))
    ;           (LST-1999 LASTCONS-1998))
    ;      (DECLARE (TYPE LIST LISTPTR-2005)
    ;               (TYPE CONS LASTCONS-1998)
    ;               (TYPE LIST LST-1999))
    ;      (TAGBODY
    ;       LL-2008
    ;        (IF (ENDP LISTPTR-2005)
    ;            (GO SERIES::END))
    ;        (SETQ ELEMENTS-2004 (CAR LISTPTR-2005))
    ;        (SETQ LISTPTR-2005 (CDR LISTPTR-2005))
    ;        (SETQ ITEMS-2002 ((LAMBDA (V-2000) (1+ V-2000)) ELEMENTS-2004))
    ;        (SETQ LASTCONS-1998
    ;                (SB-KERNEL:%RPLACD LASTCONS-1998 (CONS ITEMS-2002 NIL)))
    ;        (GO LL-2008)
    ;       SERIES::END)
    ;      (CDR LST-1999))"
    

思ったこと

  • Schemeのdefine-syntaxは、defmacroと違って再帰的に何十回も展開されることが多い。眺めていると、これらの多くの回数の殆どは、defmacroで言えば、引数をmapcarしてなんとかetc(macroexpandでは表示されない)のところに付き合っているのではないかという気がしてきた。

Common Lisp 入門 を読む (2)

Posted 2012-02-26 03:44:00 GMT

Common Lisp 入門 を読む の2回目

2. 関数の定義

この章では関数定義や良く使われる制御構造など説明されていますが、混乱しやすいところが先回りして説明されていたりして、これまた深いなと思いました。この本のようにはまりそうなところが積極的に解説されてる本はあまりないかもしれません。
面白いところとしては、KCLは、処理系を抜けるための関数として(bye)が定義されていますが、万が一の再定義のために(by)も定義されているのだとか。
この章もANSI CLと共通です。

CLでsrfi-100

Posted 2012-02-25 15:52:00 GMT

CLでsrfi、今回は、srfi-100の「define-lambda-object」です。
謎の大作が多いJoo ChurlSooさんのsrfiですが、srfi-100は、Jooさんの最新作です。

動作

(defpackage :demo
  (:use :cl :srfi-100 :srfi-89 :named-readtables))

(in-package :demo) (in-readtable :quasiquote)

(define-lambda-object ppoint (x) y)

(define-lambda-object (spoint (ppoint)) (x 0) (y x) (z x) ('stack '()) (`,pop (if (null stack) (error 'spoint "null stack" stack) (let ((s (car stack))) (setq stack (cdr stack)) s))) (,push (lambda (s) (setq stack (cons s stack)))))

(define* sp (make-spoint))

(mapcar #'sp '(x y z)) ;=> (0 0 0) (define* sp (make-spoint 5 55))

(mapcar #'sp '(x y z)) ;=> (5 55 5) (define* sp (make-spoint-by-name 'z 100 'stack (list 'sunflower)))

(mapcar #'sp '(x y z)) ;=> (0 0 100) (funcall (sp 'push) 'rose) ;=> (ROSE SUNFLOWER) (funcall (sp 'push) 'lily) ;=> (LILY ROSE SUNFLOWER) (sp 'pop) ;=> LILY (sp 'pop) ;=> ROSE (sp 'pop) ;=> SUNFLOWER

移植について

リストの作成とは無関係に使われるunquoteがありますが、リードテーブルでquasiquoteをサポートするということにしました。リーダーマクロでサポートしない場合は、unquoteと書いておけば大丈夫な筈です。(ソースからして、quasiquoteを使って書いてあるので使わない場合、修正が必要ですが…)
define-syntaxのものと、define-macroの実装がありますが、define-macroの方を移植してみました。
説明だと(foo :constructor)で帰ってくるのがリストのようですが、コードは点対リストを返すように書かれています。srfi-100をサポートしているSTklosで確認したところ点対リストが返るので、点対リストで良いやということにしました。
キーワード的なものはいつものごとく利便を考えて、keywordパッケージのシンボルにしてあります。 ■

LISP日記 2012-02-26

Posted 2012-02-25 14:59:00 GMT

分かったこと

  • 特になし

やりたいこと

  • CL系のバッククォートは、どうして、read時に解決することにしたのか知りたい。'はquoteのリーダーマクロだし、Schemeのquasiquoteも同じく。ytoolsにも書いてた気がするけど、こっちの方が色々ポータブルにいじれる気がする

みつけたもの

  • srfi-100を移植していて、「あるグループに属する関数かどうか」を判定する方法が必要になった。Racketでは、object-nameというのがあって、関数名が取得できるらしい。CLでも、なにか方法はありそうだったけど、苦肉の策でdocumentationを使ってみることにした。ポータブルな方法ってあるのだろうか。
    (defvar *objs*
      (flet ((frob1 () "*frob*" :frob1)
             (frob2 () "*frob*" :frob2)
             (quux  () (random 2)) )
        (list #'frob1 #'list #'quux 8 (list 8) #'frob2) ))

    (collect (choose-if (^x (string= "*frob*" (documentation x 'function))) (scan *objs*))) ;=> (#<FUNCTION (FLET FROB1) {101798696B}> #<FUNCTION (FLET FROB2) {1017986A7B}>)

思ったこと

  • Seriesがカオスに感じるのは、一つのことをするのに、やり方が何通りもあるってのが一つの理由な気がする。同じ方法でも記法が二通り位あったりもする。

Common Lisp 入門 を読む (1)

Posted 2012-02-23 18:38:00 GMT

Amazonで安くなった本をポチポチしていたら大分積読の本が溜ってきたので、手元の本を読破していく記録を付けてみることにしました。
一度読んだ本もあるのですが、問題が付いているようなものは全部解いて行こうかなと思います。
とりあえず手始めに、自分が初めて読んだCommon Lispの本ということで、Common Lisp 入門を読んで行きたいと思います。

まえがき

1986年当時の世情が書かれているもののCLについての説明はわかりやすく、背景も詳しく説明されています。

1. 基本構造

通常の入門書だと割と後の方に出てきそうなトピックもさらっと出てきますが、分かりやすく説明してあります。全くの入門の人はさらっと流せる感じですが、知ってる人が良く読むと細かい記述で深いです。読み返す度に発見があったりするのではないでしょうか。この章は現在のANSI CLとも共通です。

LISP日記 2012-02-23

Posted 2012-02-23 17:21:00 GMT

分かったこと

  • implicit-mapだと、lambdaの簡略記法マクロがそのまま(例えばfuncallなしで)使えるらしい)
    ;; implicit-mapあり
    (collect
      ((^x (* 3 x))
       (scan-range :from 0 :upto 100)))

    ;; implicit-mapなし (collect (#M(^x (* 3 x)) (scan-range :from 0 :upto 100)))

    こんな感じ

やりたいこと

  • 次は、SRFI-34でも移植したい。

みつけたもの

  • SBCLでseries関係をマクロ展開すると、compiler-letのインデントが妙に突出していて直したかったが、pprintの設定にcompiler-letの設定が無いのが原因のようだったので、追加したところ普通のletのように表示されるようになった。良かった良かった。
    ;; see src/code/pprint.lisp
    (set-pprint-dispatch '(cons (eql sb-cltl2:compiler-let))
                         (symbol-function 'sb-pretty::pprint-let))

思ったこと

  • ブログをS式で書けば、テンプレもマクロにできるので、もしかしたら便利かもしれないと思った。全然便利でない可能性も高い。といいつつ早速マクロを作成して使ってみる。

Older entries (1447 remaining)