#:g1: frontpage

 

com.informatimago.tools.manifestの紹介

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

(LISP Library 365参加エントリ)

 LISP Library 365 の353日目です。

com.informatimago.tools.manifestとはなにか

 com.informatimago.tools.manifestは、Pascal Bourguignon氏作のシステムの目録を作成するユーティリティです。

パッケージ情報

パッケージ名com.informatimago.tools.manifest
Quicklisp

インストール方法

(ql:quickload :com.informatimago.tools.manifest)

試してみる

 エクスポートされているシンボルは下記の通りですが、このうちで主なものはprint-manifestとwrite-manifestで、他はこれらの補助関数のようです。

  • asdf-system-license
  • asdf-system-name
  • distribution
  • executable-filename
  • executable-name
  • lisp-implementation-type-keyword
  • machine-type-keyword
  • print-manifest
  • system-depends-on
  • system-depends-on/recursive
  • write-manifest

 print-manifestは現在ロードされているsystemとホスト情報を目録にします。

(com.informatimago.tools.manifest:print-manifest :cl-ppcre)
;>>  date                        : 2014-11-20 16:34:26 -0900
;>>  lisp-implementation-type    : SBCL
;>>  lisp-implementation-version : 1.2.5
;>>  machine-type                : X86-64
;>>  machine-version             : Intel(R) Xeon(R) CPU E3-1230 v3 @ 3.30GHz
;>>  machine-instance            : t
;>>  distribution                : (LINUX DEBIAN jessie/sid)
;>>  
;>>  System    License
;>>  --------  -----
;>>  cl-ppcre  BSD-2
;>>  --------  -----
;>>  
;=>  NIL

(com.informatimago.tools.manifest:print-manifest :com.informatimago.tools.manifest) ;>> date : 2014-11-20 15:50:34 -0900 ;>> lisp-implementation-type : SBCL ;>> lisp-implementation-version : 1.2.5 ;>> machine-type : X86-64 ;>> machine-version : Intel(R) Xeon(R) CPU E3-1230 v3 @ 3.30GHz ;>> machine-instance : t ;>> distribution : (LINUX DEBIAN jessie/sid) ;>> ;>> System License ;>> -------------------------------- ----- ;>> com.informatimago.tools.manifest AGPL3 ;>> -------------------------------- ----- ;>> ;=> NIL

 write-manifestの方は、ファイルに書き出しますが、

システム名-処理系-バージョン-OS-OSのディストリビューション-マシンのアーキテクチャ.manifest

となっています。

(let ((*default-pathname-defaults* #p"/tmp/"))
  (com.informatimago.tools.manifest:write-manifest :com.informatimago.tools.manifest
                                                   :com.informatimago.tools.manifest))
Manifest for com.informatimago.tools.manifest-sbcl-1.2.5-linux-debian-jessie/sid-x86-64
---------------------------------------------------------------------------------------

date : 2014-11-20 16:28:45 -0900 lisp-implementation-type : SBCL lisp-implementation-version : 1.2.5 machine-type : X86-64 machine-version : Intel(R) Xeon(R) CPU E3-1230 v3 @ 3.30GHz machine-instance : t distribution : (LINUX DEBIAN jessie/sid)

System License -------------------------------- ----- com.informatimago.tools.manifest AGPL3 -------------------------------- -----

こんな感じに書き出されますが、上記の場合、ディストリビューション名にスラッシュが入っていてこれが排除されていないためディレクトリが作成できないというエラーになるのはご愛嬌。

まとめ

 今回は、com.informatimago.tools.manifestを紹介してみました。稼動しているsystemを確認したい時には便利そうですね。

cl-unificationの紹介

Posted 2014-12-17 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の352日目です。

cl-unificationとはなにか

 cl-unificationは、Marco Antoniotti氏作のCommon Lispでユニフィケーションを実現するライブラリです。

パッケージ情報

パッケージ名cl-unification
Quicklisp
CLiKiCLiki: cl-unification
Quickdocscl-unification | Quickdocs
common-lisp.netcl-unification
CL Test Grid: ビルド状況cl-unification | CL Test Grid

インストール方法

(ql:quickload :cl-unification)

試してみる

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

 基本的な構造はシンプルで、ユニファイする変数と環境を指定するとマッチした結果が取得できます。

(unify 42 42)
;=>  #<EMPTY UNIFY ENVIRONMENT: 1 frame {101BFAA993}>

(let ((env (unify #T(number ?x) 42))) (find-variable-value '?x env)) ;=> 42 ; T

(let ((env (unify (list 1 '?y 3) (list '?x 2 '?z)))) (list (find-variable-value '?x env) (find-variable-value '?y env) (find-variable-value '?z env))) ;=> (1 2 3)

 マッチにはテンプレートを利用できて、複合したデータ構造もこれを指定すればマッチ可能です。

(let* ((env (unify #T(number ?x) 42))
       (env (unify #T(number ?y)
                   (find-variable-value '?x env)
                   env)))
  (list (find-variable-value '?x env)
        (find-variable-value '?y env)))
;=>  (42 42)

(defclass makanito () ((a :accessor a) (b :accessor b)))

(defvar *obj* (make-instance 'makanito))

(setf (a *obj*) 42 (b *obj*) 43)

(describe *obj*) ;>> #<MAKANITO {10203724D3}> ;>> [standard-object] ;>> ;>> Slots with :INSTANCE allocation: ;>> A = 42 ;>> B = 43 ;>> ;=> <no values>

(let ((env (unify #T(makanito a ?a b ?b) *obj*))) (list (v? '?a env) (v? '?b env))) ;=> (42 43)

#Tというのがテンプレート用のリーダーマクロでデータ型名とスロットを指定します。
また、expression-templateというものの場合、読み出し手続きの結果として値が返ってきます。

(let ((env (unify (*:iota 100) #T(elt 3 ?x))))
  (find-variable-value '?x env))
;=>  3
;    T

 さらに、制御構文としてパタンで分岐する各種matchがあります。

(defun h (op a b)
  (match-case ((list op a b))
    ('(+ 
       #T(number ?M)
       #T(number ?N))
     (+ M N))
    ('(+ 0 ?F)  F)
    ('(+ ?F 0)  F)
    ('(+ ?A (+ ?B ?C))  (h '+ (h '+ A B) C))
    ((list '*
           #T(number ?M)
           #T(number ?N))
     (* M N))
    ('(* 0 ?F)  0)
    ('(* ?F 0)  0)
    ('(* ?F 1)  F)
    ('(* 1 ?F)  F)
    ('(* ?A (* ?B ?C))  (h '* (h '* A B) C))
    ('(?Op ?A ?B)  (list Op A B))))

(defun harropify (x) (match-case (x) ('(?Op ?A ?B) (h Op (harropify A) (harropify B))) (?A A)))

(harropify '(* (+ 1 2) (+ (* x 0) y))) ;=> (* 3 Y)

まとめ

 今回は、cl-unificationを紹介してみました。
双方向マッチを使わないとあまり旨味がない気がしますが、テンプレートを拡張できるのは便利ですね。

Allegro CL: LLの紹介

Posted 2014-12-16 15:00:00 GMT

(LISP Library 365参加エントリ)
(Lisp Advent Calendar 2014参加エントリ)

 LISP Library 365 の351日目、Lisp Advent Calendar 2014の17日目です。

Allegro CL: LLとはなにか

 Allegro CL: LLは、Allegro CLで低レベルを記述する仕組みです。

パッケージ情報

パッケージ名Allegro CL: LL
ドキュメントILC 2007 Tutorial: Optimizing and Debugging Programs in Allegro CL - LL

インストール方法

 Allegro CL標準の機能です。compilerパッケージに定義されているようです。

試してみる

 LLというのは、何の略なのかというところですが、`attempt to perform low-level compiler-only :+ operation on NIL.'というエラーメッセージが出るところからすると、Low-Levelの略でしょうか。
Allegro CLにはLAPを直に書く機能もありますが、LLは、もう少しLispの世界と行ったり来たりがしやすくありつつ、インストラクションには大体1対1で対応した命令があるようです。なお、キャリーフラグ等の状態も取得できます。詳細は上記リンクの文献を参照してください。
ということで、簡単なコードでも書いてみます。

 以前、SBCLでVOPの実験をした時にfactの高速化を試みたことがありましたが、繰り返しでfactを書いてdisassembleしてみたところ、SBCLと同様に、繰り返し内でimulする度にfixnumと即値を変換していたので、これを出入口で一つにまとめてみたいと思います。

(defun fact-loop (n)
  (declare (optimize speed (safety 0) (debug 0)))
  (declare (fixnum n))
  (prog ((a 1))
        (declare (fixnum a))
     L0 (cond ((zerop n)
               (go L1 )))
        (setq a (* a n))
        (decf n)
        (go L0)
     L1 (return a)))

(fact-loop 30) ;=> 458793068007522304

;; code start: #x1002a140d8: 0: 49 c7 c5 08 00 movq r13,$8 ; 1 00 00 7: eb 0c jmp 21 9: 49 c1 fd 03 sar r13,$3 13: 4c 0f af ef imulq r13,rdi 17: 48 83 ef 08 sub rdi,$8 21: 48 83 ff 00 cmp rdi,$0 25: 75 ee jnz 9 27: 49 8b fd movq rdi,r13 30: f8 clc 31: 4c 8b 74 24 10 movq r14,[rsp+16] 36: c3 ret 37: 90 nop

 これをLLを使ってこんな感じに書いてみました。

(defun fact-loop/ (n)
  (declare (optimize speed (safety 0) (debug 0)))
  (declare (fixnum n))
  (prog ((a (comp::ll :fixnum-to-mi 1))
         (n (comp::ll :fixnum-to-mi n)))
     L0 (cond ((comp::ll := 0 n)
               (go L1 )))
        (setq a (comp::ll :* a n))
        (setq n (comp::ll :- n (comp::ll :fixnum-to-mi 1)))
        (go L0)
     L1 (return (comp::ll :mi-to-fixnum a))))

;; disassembly of #<Function FACT-LOOP/> ;; formals: N ;; code start: #x1001084a98: 0: 49 c7 c5 01 00 movq r13,$1 00 00 7: 48 c1 ff 03 sar rdi,$3 11: eb 08 jmp 21 13: 4c 0f af ef imulq r13,rdi 17: 48 83 ef 01 sub rdi,$1 21: 48 83 ff 00 cmp rdi,$0 25: 75 f2 jnz 13 27: 49 8b fd movq rdi,r13 30: 48 c1 e7 03 sal rdi,$3 34: f8 clc 35: 4c 8b 74 24 10 movq r14,[rsp+16] 40: c3 ret 41: 90 nop

 速度を比べてみます。

(dotimes (i (expt 10 8))
  (fact-loop 60))
;=> NIL
#|------------------------------------------------------------|
; cpu time (non-gc) 5.516000 sec user, 0.000000 sec system
; cpu time (gc)     0.000000 sec user, 0.000000 sec system
; cpu time (total)  5.516000 sec user, 0.000000 sec system
; real time  5.516957 sec
; space allocation:
;  54 cons cells, 5,232 other bytes, 0 static bytes
x86_64
 |------------------------------------------------------------|#

(dotimes (i (expt 10 8)) (fact-loop/ 60)) ;=> NIL #|------------------------------------------------------------| ; cpu time (non-gc) 3.640000 sec user, 0.000000 sec system ; cpu time (gc) 0.000000 sec user, 0.000000 sec system ; cpu time (total) 3.640000 sec user, 0.000000 sec system ; real time 3.640540 sec ; space allocation: ; 27 cons cells, 2,640 other bytes, 0 static bytes x86_64 |------------------------------------------------------------|#

少し速くなりました。

まとめ

 今回は、Allegro CL: LLを紹介してみました。
SBCL/CMUCLのVOPより手軽に使えて書き易い気がしました。通常の式と混ぜられるのが良いですね。
明日のLisp Advent Calendar 2014は、@y2q_actionmanさんです。お楽しみに。

jsminの紹介

Posted 2014-12-15 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の350日目です。

jsminとはなにか

 jsminは、Mario Domenech Goulart氏作のJavaScriptを圧縮するユーティリティです。Douglas Crockford氏のjsminを移植したものとのこと。

パッケージ情報

パッケージ名jsmin
Chicken eggs:jsmin - The Chicken Scheme wiki

インストール方法

$ sudo chicken-install jsmin

すれば、

(use jsmin)

で使えます。

試してみる

 構成は非常にシンプルで、jsmin-stringと、jsmin-fileの2つのみ。
その名の通り、jsmin-stringは文字列を、jsmin-fileはファイルを変換します。

(jsmin-string "function fib (n) {
    if (n < 2) {
        return n;
    } else {
        return fib(n - 1) + fib(n - 2);
    }
}")
;=> "\nfunction fib(n){if(n<2){return n;}else{return fib(n-1)+fib(n-2);}}"

まとめ

 今回は、jsminを紹介してみました。
150行程みたいなので、他の処理系やLisp方言に移植するのも簡単そうですね。

inotifyの紹介

Posted 2014-12-14 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の349日目です。

inotifyとはなにか

 inotifyは、Olof-Joachim Frahm氏作のLinuxのファイルシステムイベントを監視するinotifyをCommon Lispから利用可能にするライブラリです。

パッケージ情報

パッケージ名inotify
Quicklisp
Quickdocsinotify | Quickdocs
CL Test Grid: ビルド状況inotify | CL Test Grid

インストール方法

(ql:quickload :inotify)

試してみる

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

 使い方はシンプルで、with-inotify内でイベントを監視します。

(inotify:with-inotify (inot `((#P"/tmp/" ,inotify:in-create)))
  (inotify:read-events inot))

; and ... (with-open-file (out "/tmp/bar" :if-does-not-exist :create))

;=> (#S(INOTIFY:EVENT ; :WATCH #<INOTIFY:WATCH pathname: #P"/tmp/" mask: (IN-CREATE)> ; :MASK (INOTIFY:IN-CREATE) ; :COOKIE 0 ; :NAME "bar"))

上記のように監視中に指定の場所でイベントが発生(上記の場合書き込み)すると通知されます。
タイムアウトも指定可

(inotify:with-inotify (inot `((#P"/tmp/" ,inotify:in-create)))
  (inotify:read-events inot :time-out 1))
;=>  NIL

まとめ

 今回は、inotifyを紹介してみました。
Linuxのファイルシステムがこういうものをサポートしていたとは知りませんでしたが、これは便利ですね。

yaclmlの紹介

Posted 2014-12-13 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の348日目です。

yaclmlとはなにか

 yaclmlは、Marco Baringer氏作のHTMLテンプレートエンジンです。

パッケージ情報

パッケージ名yaclml
Quicklisp
CLiKiCLiki: yaclml
Quickdocsyaclml | Quickdocs
CL Test Grid: ビルド状況yaclml | CL Test Grid

インストール方法

(ql:quickload :yaclml)

試してみる

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

 Common LispのHTMLテンプレートエンジンには色々ありますが、cl-whoあたりがメジャーなところでしょうか。
やはりLispなのでS式で書きたいわけなのですが、yaclmlは、デザイナーにも優しいことも設計目標の一つらしいです。

 HTMLタグをキーワードシンボルで表現するcl-who等と違って

(<:tag ...)

 という風にHTML風の<パッケージにタグを定義していきます。
タグは関数/マクロになっているのでユーザーの拡張も簡単です。
こんな感じに書いたテンプレートがあったとすると、

(<:html
 (<:body
  (<:table
   (let ((col 0))
     (<:tr (<:td [~(incf col)]) (<:td [~(incf col)]) (<:td [~(incf col)]))
     (<:tr (<:td "foo") (<:td ["bar"]) (<:td "3"))))
  (<::unordered-list2 "foo" "bar" "baz")))

 これをこんな感じに呼び出せば、

(defmacro <::unordered-list (&body list)
  `(<:ul ,@(mapcar (lambda (x) `(<:li ,x)) list)))

(yaclml:with-yaclml-stream *standard-output* (with-open-file (in "/var/tmp/yaclml.lisp") (let ((*readtable* (copy-readtable nil))) (yaclml:enable-yaclml-syntax) (eval (read in))))) ;>> <html ;>> ><body ;>> ><table ;>> ><tr ;>> ><td ;>> >1</td ;>> ><td ;>> >2</td ;>> ><td ;>> >3</td ;>> ></tr ;>> ><tr ;>> ><td ;>> >foo</td ;>> ><td ;>> >&quot;bar&quot;</td ;>> ><td ;>> >3</td ;>> ></tr ;>> ></table ;>> ><ul ;>> ><li ;>> >foo</li ;>> ><li ;>> >bar</li ;>> ><li ;>> >baz</li ;>> ></ul ;>> ></body ;>> ></html ;>> > ;=> <no values>

という感じに出力されます。

 テンプレートの方はTALという名前らしいですが、「[」から「]」の間で、

  • 「~」 → Lisp式を評価して表示
  • 「"」 → quot;に置換
  • 「$」 変数を評価(表示はなし)
  • 「@」 リストを展開(表示はなし)

の特殊文字が使えます。

まとめ

 今回は、yaclmlを紹介してみました。
このブログもyaclmlで書いていますが、拡張はしやすい気はしています。

リストの最後にpushする

Posted 2014-12-13 14:59:00 GMT

 リストの構造的に最後に要素を追加するということはあまり無い訳ですが、リストの最後にpushするというイディオムもまた、ぱっとしたものはないなあと思った次第。
なんとなく、nconcのことが多い気もします。

(let ((x (*:iota 10)))
  (push :foo (cdr (last x)))
  x)
;=>  (0 1 2 3 4 5 6 7 8 9 :FOO)

(let ((x (*:iota 10))) (setf (cdr (last x)) (list :foo)) x) ;=> (0 1 2 3 4 5 6 7 8 9 :FOO)

(define-modify-macro nconcf (&rest lists) nconc)

(let ((x (*:iota 10))) (nconcf x (list :foo)) x) ;=> (0 1 2 3 4 5 6 7 8 9 :FOO)

com.informatimago.tools.symbolの紹介

Posted 2014-12-11 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の346日目です。

com.informatimago.tools.symbolとはなにか

 com.informatimago.tools.symbolは、Pascal Bourguignon氏作のシンボルを扱うユーティリティです。

パッケージ情報

パッケージ名com.informatimago.tools.symbol
Quicklisp

インストール方法

(ql:quickload :com.informatimago.tools.symbol)

試してみる

 現在のところ定義されているのは2つの関数のみで、check-duplicate-symbolsと、duplicate-symbolsがあります。

(com.informatimago.tools.symbol:duplicate-symbols
 :packages (mapcar #'find-package '(:cl :arc)))
;=>  ((ARC:WRITE WRITE) (ARC:WHEN WHEN) (ARC:UNLESS UNLESS) (ARC:UNION UNION)
;     (ARC:TYPE TYPE) (ARC:TIME TIME) (ARC:THROW THROW) (ARC:TAN TAN)
;     (ARC:SUBST SUBST) (ARC:STRING STRING) (ARC:SORT SORT) (ARC:SLEEP SLEEP)
;     (ARC:SIN SIN) (ARC:SET SET) (ARC:REM REM) (ARC:REDUCE REDUCE) (ARC:READ READ)
;     (ARC:PUSHNEW PUSHNEW) (ARC:OR OR) (ARC:NUMBER NUMBER) (ARC:NTHCDR NTHCDR)
;     (ARC:MISMATCH MISMATCH) (ARC:MIN MIN) (ARC:MERGE MERGE) (ARC:MAX MAX)
;     (ARC:MAP MAP) (ARC:LOOP LOOP) (ARC:LOG LOG) (ARC:LIST LIST) (ARC:LET LET)
;     (ARC:LAST LAST) (ARC:IF IF) (ARC:GET GET) (ARC:FIND FIND)
;     (ARC:DO
;       DO)
;     (ARC:COUNT COUNT) (ARC:COS COS) (ARC:CONS CONS) (ARC:COMPLEMENT COMPLEMENT)
;     (ARC:COERCE COERCE) (ARC:CHAR CHAR) (ARC:CDDR CDDR) (ARC:CATCH CATCH)
;     (ARC:CASE CASE) (ARC:CADR CADR) (ARC:CAAR CAAR) (ARC:ATOM ATOM)
;     (ARC:ASSOC ASSOC) (ARC:ASSERT ASSERT) (ARC:APPLY APPLY) (ARC:AND AND)
;     (ARC:ADJOIN ADJOIN) (ARC:ACONS ACONS) (ARC:>= >=) (ARC:> >) (ARC:= =)
;     (ARC:<= <=) (ARC:< <) (ARC:++ ++) (ARC:+ +))

(com.informatimago.tools.symbol:check-duplicate-symbols :packages (mapcar #'find-package '(:cl :arc))) ;>> ((ARC:WRITE CL:WRITE) (ARC:WHEN CL:WHEN) (ARC:UNLESS CL:UNLESS) ;>> (ARC:UNION CL:UNION) (ARC:TYPE CL:TYPE) (ARC:TIME CL:TIME) ;>> (ARC:THROW CL:THROW) (ARC:TAN CL:TAN) (ARC:SUBST CL:SUBST) ;>> (ARC:STRING CL:STRING) (ARC:SORT CL:SORT) (ARC:SLEEP CL:SLEEP) ;>> (ARC:SIN CL:SIN) (ARC:SET CL:SET) (ARC:REM CL:REM) (ARC:REDUCE CL:REDUCE) ;>> (ARC:READ CL:READ) (ARC:PUSHNEW CL:PUSHNEW) (ARC:OR CL:OR) ;>> (ARC:NUMBER CL:NUMBER) (ARC:NTHCDR CL:NTHCDR) (ARC:MISMATCH CL:MISMATCH) ;>> (ARC:MIN CL:MIN) (ARC:MERGE CL:MERGE) (ARC:MAX CL:MAX) (ARC:MAP CL:MAP) ;>> (ARC:LOOP CL:LOOP) (ARC:LOG CL:LOG) (ARC:LIST CL:LIST) (ARC:LET CL:LET) ;>> (ARC:LAST CL:LAST) (ARC:IF CL:IF) (ARC:GET CL:GET) (ARC:FIND CL:FIND) ;>> (ARC:DO ;>> CL:DO) ;>> (ARC:COUNT CL:COUNT) (ARC:COS CL:COS) (ARC:CONS CL:CONS) ;>> (ARC:COMPLEMENT CL:COMPLEMENT) (ARC:COERCE CL:COERCE) (ARC:CHAR CL:CHAR) ;>> (ARC:CDDR CL:CDDR) (ARC:CATCH CL:CATCH) (ARC:CASE CL:CASE) (ARC:CADR CL:CADR) ;>> (ARC:CAAR CL:CAAR) (ARC:ATOM CL:ATOM) (ARC:ASSOC CL:ASSOC) ;>> (ARC:ASSERT CL:ASSERT) (ARC:APPLY CL:APPLY) (ARC:AND CL:AND) ;>> (ARC:ADJOIN CL:ADJOIN) (ARC:ACONS CL:ACONS) (ARC:>= CL:>=) (ARC:> CL:>) ;>> (ARC:= CL:=) (ARC:<= CL:<=) (ARC:< CL:<) (ARC:++ CL:++) (ARC:+ CL:+)) ;>> ;=> <no values>

こんな感じに同名のシンボルを列挙してくれます。
check-duplicate-symbolsが表示ユーティリティで、リスト得たい場合には、duplicate-symbolsを使うという感じですね。

まとめ

 今回は、com.informatimago.tools.symbolを紹介してみました。パッケージ定義の時や、ライブラリのコードを読む前に確認したりする際に便利に使えそうですね。

CLISP: Weak Objectsの紹介

Posted 2014-12-10 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の345日目です。

CLISP: Weak Objectsとはなにか

 CLISP: Weak Objectsは、CLISPの弱参照を扱う仕組みです。

パッケージ情報

パッケージ名CLISP: Weak Objects
ドキュメント31.7. Weak Objects

インストール方法

 CLISP標準の機能です。extパッケージで定義されています。

試してみる

 CLISP: Weak Objectsがサポートしている各種形式ですが、割合に充実しています。

  • Weak Pointers
  • Weak Lists
  • Weak “And” Relations
  • Weak “Or” Relations
  • Weak Associations
  • Weak “And” Mappings
  • Weak “Or” Mappings
  • Weak Association Lists
  • Weak Hash Tables

 最も基本になるのは、Weak Pointersですが、こんな感じにGC時に参照されていなければ消えます。

(defvar *wp*
  (ext:make-weak-pointer (list 1)))

(ext:weak-pointer-value *wp*) ;=> (1) ; T

(gc) ;=> 6540000 ; 1635000 ; 164808 ; 414 ; 460917632 ; 1032000

*wp* ;=> #<BROKEN WEAK-POINTER>

(ext:weak-pointer-value *wp*) ;=> NIL ; NIL

 この仕組みでリスト、alist、ハッシュテーブルの弱参照版が作られていますが、リストの場合は、weak pointerでリストを作るよりは効率が良いそうです。

(defvar *a* (list 'a))

(defvar *weak-list* (ext:make-weak-list (list *a* (list 'b) (list 'c))))

(ext:weak-list-list *weak-list*) ;=> ((A) (B) (C))

(gc) ;=> 6523344 ; 1630836 ; 164808 ; 403 ; 445232856 ; 920000

*weak-list* ;=> #<WEAK-LIST ((A))>

(ext:weak-list-list *weak-list*) ;=> ((A))

 Weak Relationsのorとandは、リスト内で参照されているものが一つでもあれば、全体が生きるのが、or、全部生きていなければ破棄されるのがandです。

(defvar *a* (list 'a))

(defvar *weak-and* (ext:make-weak-and-relation (list *a* (list 'b) (list 'c))))

*weak-and* ;=> #<WEAK-AND-RELATION ((A) (B) (C))>

(gc) ;=> 6521192 ; 1630298 ; 164808 ; 395 ; 432755648 ; 852000

*weak-and* ;=> #<BROKEN WEAK-AND-RELATION>

(defvar *weak-or* (ext:make-weak-or-relation (list *a* (list 'b) (list 'c)))) ;=> *WEAK-OR* *weak-or* ;=> #<WEAK-OR-RELATION ((A) (B) (C))>

(gc) ;=> 6521112 ; 1630278 ; 164808 ; 396 ; 434142344 ; 880000

*weak-or* ;=> #<WEAK-OR-RELATION ((A) (B) (C))>

 Weak Associationは、keyとvalueでkeyが生きていれば、組の値も参照できるというものです。

(defvar *key* (list 1))

(defvar *wa* (ext:make-weak-mapping *key* :value))

(ext:weak-mapping-value *wa*) ;=> :VALUE (setq *key* nil)

(gc) ;=> 6523888 ; 1630972 ; 164808 ; 429 ; 484286112 ; 1144000

*wa* ;=> #<BROKEN WEAK-MAPPING>

(ext:weak-mapping-value *wa*) ;=> NIL

仕組みの紹介としてはこんな感じですが、ユーティリティも充実していて使い勝手が良さそうです。ユーティリティの詳細はドキュメントと参照のこと。

まとめ

 今回は、CLISP: Weak Objectsを紹介してみました。
ざっと最近の処理系を見渡したところ弱参照のサポートの充実具合は実はCLISPが一番のようです。
CLISPは地味に充実しているところがあったりしますね。

let-by-needの紹介

Posted 2014-12-09 15:00:00 GMT

(LISP Library 365参加エントリ)
(Lisp Advent Calendar 2014参加エントリ)

 LISP Library 365 の344日目、Lisp Advent Calendar 2014の10日目です。

let-by-needとはなにか

 let-by-needは、Martin Abadi氏作のマクロによる式変形で遅延評価を実現するものです。

パッケージ情報

パッケージ名let-by-need
ソースコードLETBYN.LSP[COM,LSP]-www.SailDart.org

インストール方法

 上記ソースコードをダウンロードして適当に動かします。
MacLISP用のようですが、Common Lispでもstatusを定義してやればそのまま動きます(statusを削るのも可)

(defmacro status (thing &rest args)
  `(case ',thing
     (feature (member (car ',args) *features*))
     (otherwise nil)))

試してみる

 saildartのCommon Lispのディレクトリを眺めていて見付けたのですが、割合に面白そうなので紹介してみることにしました。1982年のコードなので32年前のコードですね。
どんなものかは動作をみるとわかりやすいのですが、

(let-by-need ((a (print 'a))
              (b (print nil))
              (c (print 'c)))
  (and a b c))
;>>  
;>>  A 
;>>  NIL 
;=>  NIL

こんな感じに遅延評価的にletの束縛部のcは使われていないので評価されないというものです。

 マクロの種類としては、構文の乗っ取り型でボディの中身を書き換えてしまいます。

(let-by-need ((a t)
              (b nil)
              (c t))
  (if (or a b)
      (and a c)
      (if a b c)))

;==> (LET ((A T)) (COND (A (AND A (LET ((C T)) C))) (T (LET ((B NIL)) (COND (B (AND A (LET ((C T)) C))) (T (AND T (COND (A B) (T (AND T (LET ((C T)) C)))))))))))

 let-by-needが把握できる式変形は、関数呼び出し、lambda式、if、cond、and、orです。

(let-by-need ((a (print 'a))
              (b (print nil))
              (c (print 'c)))
  (if a c b))
;>>  
;>>  A 
;>>  C 
;=>  C

;===>
(LET ((A (PRINT 'A)))
  (COND
   (A
    (LET ((C (PRINT 'C)))
      C))
   (T
    (AND T
         (LET ((B (PRINT NIL)))
           B)))))

(let-by-need ((a (print 'a))(b nil)(c (print 'c))) (lambda () a)) ;=> #<FUNCTION (LAMBDA ()) {1016F712AB}> ;===> (LAMBDA () (LET ((A (PRINT 'A))) A))

(let-by-need ((a (print 'a)) (b (print nil)) (c (print 'c))) (list a (list c))) ;>> ;>> A ;>> C ;=> (A (C)) ;===> (LET ((A (PRINT 'A)) (C (PRINT 'C))) (LIST A (LIST C)))

まとめ

 今回は、let-by-needを紹介してみました。
遅延評価のために元の式の方をマクロで書き換えてしまうというのはありそうでない方式ですね。
明日のLisp Advent Calendar 2014は、@y2q_actionmanさんです。お楽しみに。

Older entries (1865 remaining)