#:g1: frontpage

 

coopsの紹介

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

(LISP Library 365参加エントリ)

 LISP Library 365 の245日目です。

coopsとはなにか

 coopsは、Felix Winkelmann氏作のChicken用のオブジェクト指向システムです。Tiny CLOSのChicken版もありますが、Chickenとしてはこっちが推奨とのことです。

パッケージ情報

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

インストール方法

$ sudo chicken-install coops

すれば、

(use coops)

(require-extension coops)

 基本的なクラスが coops-primitive-objects モジュールになっているので、こっちもuseして使うと良いかなと思います。

試してみる

 coopsの大元は、Dorai Sitaram氏のCLOS風のオブジェクト指向システムのScmObjとのことで、これを拡張したものだそうです。ScmObjを眺めてみると、こっちの方は命名規則等がCLOSっぽい様子。

 CLOS風ということで大体想像が付く所ですが、

  • 総称関数
  • 多重継承
  • マルチメソッド
  • 一般化されたセッター(SRFI-17)
  • メソッドコンビネーション

がサポートされています。

 コードの文法的な特徴としては、メソッドの定義の括弧の位置がTiny CLOS系と違うというのがあります。
Tiny CLOS系(S式時代のDylanも)は、Common Lispのように

(define-method foo ((x <class>)) ...)

ですが、coopsは、

(define-method (foo (x <class>)) ...)

のようにSchemeの関数定義と合せてあります。
元になったScmObjもTiny CLOS系なのでcoopsでの変更の様子。

 また、メソッドコンビネーションは、

(define-method (latumapic) 
  (write-line "おはよう日本!"))


(latumapic)
;>> おはよう日本!


(define-method (latumapic before:) 
  (write-line "== before"))


(define-method (latumapic after:) 
  (write-line "== after"))


(define-method (latumapic around:) 
  (write-line "==>> around")
  (call-next-method)
  (write-line "==<< around"))


(latumapic)
;>> ==>> around
;>> == before
;>> おはよう日本!
;>> == after
;>> ==<< around

のような感じで、Common Lispの人にはお馴染の動作です。

 ということで今回もBankAccountを書いてみます。

(use coops
     coops-primitive-objects
     miscmacros)


(define-class <bank-account> ()
  ((dollars initform: 0 accessor: dollars)))


(define-generic (deposit a x))


(define-generic (withdraw a x))


(define-method (deposit (a <bank-account>) (x <integer>))
  (inc! (dollars a) x))


(define-method (withdraw (a <bank-account>) (x <integer>))
  (dec! (dollars a) (min x (dollars a))))


(define *my-account* (make <bank-account> 'dollars 200))


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


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


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


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


(define-class <stock-account> (<bank-account>)
  ((num-shares initform: 0 accessor: num-shares)
   (price-per-share initform: 30 accessor: price-per-share)))


(define-method ((setter dollars) (o <stock-account>) (x <integer>))
  (set! (num-shares o) (/ x (price-per-share o)))
  (dollars o))


(define-method (dollars (o <stock-account>))
  (* (num-shares o) (price-per-share o)))


(define *my-stock* (make <stock-account> 'num-shares 10))


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


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


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


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


(withdraw *my-stock* 120)
;=> 540


(num-shares *my-stock*)
;=> 18

という感じで、ほぼCLOSと同じ感覚で書けます。

まとめ

 今回は、coopsを紹介してみました。
このオブジェクト指向システムにどこまで拡張性があるのかは調べていませんが、通常使う分にはこれで問題なさそうですね。

VAXFlavorsの紹介

Posted 2014-08-31 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の244日目です。

VAXFlavorsとはなにか

 VAXFlavorsは、VAX LISPに付属していたFlavorsの実装です。当時DECのDave Wickert氏がメインで開発していた様子。

パッケージ情報

パッケージ名VAXFlavors
Quicklisp×

インストール方法

 旧DECのVMSでは一定の期間が経過したソフトウェアはVMS Retired Softwareという扱いになり、愛好家が趣味で利用/配布する分には問題ないものとなるようです。
VAX LISP 3.1も配布している人がいるのですが、この中にVAXFlavorsが含まれていますのでダウンロードします。

unzipするとVMSのテープイメージのファイルなのでここから更にファイルを取り出します。有り難いことにvmsbackupというツールがあるので、これを利用します。

$ vmsbackup -x -b 9216 -f lisp031.a

以下、b..c..dと続く

 これでファイルが取り出せますが、残念ながらflvtrace.lspvfが破損している様子。
たまたまVMSの実機上にVAX LISPをインストールしているサイトがあったので破損箇所を確認することができましたが、こんな感じです。

;;;
;;; -------------------------------------------------------------------------
;;;
;;; User accessed macro/function calls.
;;;


;;; Trace a method.
(defmacro trace-method (&rest val)
  "TRACE-METHOD (flavor-name1 [type] :message1)
              (flavor-name2 [type] :message2) ...

   where type is :AFTER :PRIMARY :BEFORE :AND :OR :OVERRIDE
                 :WHOPPER :DEFAULT or :CASE (defaults to :PRIMARY)

 This macro will set the requested method as being traced.
 TRACE-METHOD with no arguments will list the current methods
 being traced."

  `(flavors-trace-common t ',val))

;;; Untrace a method.
(defmacro untrace-method (&rest val)
  "UNTRACE-METHOD (flavor-name1 [type] :message1)
                (flavor-name2 [type] :message2) ...

   where type is :AFTER :PRIMARY :BEFORE :AND :OR :OVERRIDE
                 :WHOPPER :DEFAULT or :CASE (defaults to :PRIMARY)

 This macro will clear the requested method from being traced.
 UNTRACE-METHOD with no arguments clear all methods currently
 being traced."

  `(flavors-trace-common nil ',val))

;;; Trace a flavor.
(defun trace-flavor (&rest flavor-list)
  "TRACE-FLAVOR flavor1 flavor2 ...

 This function enables tracing of all methods on the indicated flavors."

  (dolist (flavor-name flavor-list)
    (let ((flavor-obj (flavors-validate-flavor-name flavor-name)))
      (setf (flavor-trace-marker flavor-obj) *flavor-trace-marker*))))

;;; Untrace a flavor.
(defun untrace-flavor (&rest flavor-list)
  "UNTRACE-FLAVOR flavor1 flavor2 ...

 This function disables tracing for the flavors.  Methods being traced
 via TRACE-METHOD will still be traced."

  (dolist (flavor-name flavor-list)
    (let ((flavor-obj (flavors-validate-flavor-name flavor-name)))
      (setf (flavor-trace-marker flavor-obj) nil))))

;;; Trace all flavors.
(defun trace-all-flavors ()
  "TRACE-ALL-FLAVORS

 Enables tracing for all flavors.  To disable, use UNTRACE-METHODS."

  (setf *flavor-trace-marker* t))

;;; *EOF*

末尾にvfが付いているのがVAXFlavors関係のファイルなので適当に動くように修正します。

試してみる

 今回もお馴染のBankAccountです。

(defpackage :vaxflavors.demo
  (:use :cl :vaxflavors)
  (:shadowing-import-from :vaxflavors
                          :defmethod :make-instance :method
                          :make-method))


(cl:in-package :vaxflavors.demo)


;;; 再定義でCERRORを出す => T / 出さない => NIL
(defparameter *error-out* nil)


(defflavor bank-account ((dollars 0)) ()
  :initable-instance-variables
  :gettable-instance-variables
  :settable-instance-variables)


(defmethod (bank-account :deposit) (x)
  (send self :set-dollars (+ (send self :dollars) x)))


(defmethod (bank-account :withdraw) (x)
  (send self :set-dollars (max 0 (- (send self :dollars) x))))


(defparameter *my-account* (make-instance 'bank-account :dollars 200))


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

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

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

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


(defflavor stock-account ((num-shares 0) (price-per-share 30))
                         (bank-account)
  :initable-instance-variables
  :gettable-instance-variables
  :settable-instance-variables)


(defmethod (stock-account :set-dollars) (x)
  (setq num-shares (/ x price-per-share))
  (send self :dollars))


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


(defparameter *my-stock* (make-instance 'stock-account :num-shares 10))


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


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


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


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


(send *my-stock* :withdraw 120)
;=> 540


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

 デフォルトだとフレイバーとメソッドの再定義はCERRORが出るようになっていますが、*ERROR-OUT*をNILにすることで抑制可能です。

まとめ

 今回は、VAXFlavorsを紹介してみました。
VAXFlavorsは当時の評判によるとMITのオリジナルのFlavorsとの互換性が高かったようです。
それはさておき2、30年前のCommon Lispのソースをコンパイルしてみていつも思うのは、当時のコンパイラの構文チェックや型チェックはゆるかったんだなということですね。

release-reportの紹介

Posted 2014-08-30 15:30:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の243日目です。

release-reportとはなにか

 release-reportは、Zach Beane氏作のQuicklispのシステム一覧をhtmlに出力するユーティリティです。

パッケージ情報

パッケージ名release-report
Quicklisp×

インストール方法

 GitHubで公開されているので拾ってきて、Quicklispのlocal-projectsにでも設置すれば、

(ql:quickload :release-report)

でロードできます。

試してみる

 そもそもこのユーティリティは、Quicklisp beta releasesのページを生成するためのものとのこと。

(let ((dist (ql-dist:find-dist "quicklisp")))
  (release-report::release-report (ql-dist:name dist)
                                  (merge-pathnames
                                   (concatenate 'string (ql-dist:name dist) "-" (ql-dist:version dist) ".html")
                                   #p"/tmp/")))

こんな感じで実行すると、/tmp/quicklisp-2014-07-14.html に一覧のhtmlが生成されます。

まとめ

 今回は、release-reportを紹介してみました。
Quicklispのdistなどをどう扱ったら良いかの例として眺めてみるのも良いかなと思います。

lowdownの紹介

Posted 2014-08-28 16:15:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の241日目です。

lowdownとはなにか

 lowdownは、Moritz Heidkamp氏作のChickenでMarkdownを扱うライブラリです。

パッケージ情報

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

インストール方法

$ sudo chicken-install lowdown

すれば、

(use lowdown)

(require-extension lowdown)

試してみる

 派生形式が色々とあるmarkdownですがlowdownは、MarkdownTest suite (version 1.0.3)には全部パスしているとのこと。
また、Schemeらしくsxml形式への変換もサポートしていたりします。

(markdown->html
 "### おはよう日本について
おはよう日本
")
;=> "<h3>おはよう日本について</h3>
;    <p>おはよう日本</p>#t"
;   #<parser-input-end>


(markdown->sxml
 "### おはよう日本について
おはよう日本
")
;=> ((h3 ("おはよう日本について")) (p "おはよう日本"))
;   #<parser-input-end>

 用意されている関数は、

  • markdown->html
  • markdown->sxml*
  • markdown-sxml->html-sxml
  • markdown->sxml
  • markdown-html-conversion-rules*

のようなところ。

まとめ

 今回は、lowdownを紹介してみました。
lowdownは、Chicken製のブログシステムで使われたりしているようです。

onlispの紹介

Posted 2014-08-28 10:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の240日目です。

onlispとはなにか

 onlispは、ポール・グレアム氏のOn Lispに出てくるコードをLiam M. Healy氏がCLOCCの一部として纏めたものです。

パッケージ情報

パッケージ名onlisp
プロジェクトサイト CLOCC - Common Lisp Open Code Collection / Hg / [e6b24a] /src/onlisp/package.lisp

インストール方法

 CLOCCのプロジェクトサイトからダウンロードしてきて適当に導入します。

$ hg clone http://hg.code.sf.net/p/clocc/hg clocc-hg

で全体のソースも取得できます。
src/onlisp が目的のソースです。

試してみる

 onlisp-util.lispと、onlisp-app.lispに分かれていますが、onlisp-util.lispの方はユーティリティとして使えなくもない感じになっています。といっても本のコードを並べただけに近いので、利用に当ってもう少し整理する必要があるかなと思います。
パッケージ名は、onlisp等ではなく、何故かpaul-graham〈ニックネームはpg〉。

(pg:flatten (funcall (5am:gen-tree :size 10)))
;=>  (-9 5 7 -6 3 -9 -5 -4 0 3 8 2 5 9 2 5 0 -5 -7 2 7 3 -3 -7 5 -1 -6 -3 4 -6 -5 3
;     9 -2 -6 6 2 6 10 -7 7 6 7 -3 9 -3 8 10 -9 10 5 0 -6 3 -7 -1 -1 1 10 9 -10 3 9
;     9 -1 -1 9 -8 -1 -4 -4)


(pg:aif 3
        pg:it)
;=>  3


(pg:mklist 42)
;=>  (42)

まとめ

 今回は、onlispを紹介してみました。
On Lispで紹介されているユーティリティは色々なユーティリティライブラリに取り込まれていますが、パッケージとしてまとめて配布しているのは、CLOCCだけのようです。
折角なのでもうちょっと使い易くして欲しいところ。

Allegro CL: Flavorsの紹介

Posted 2014-08-27 14:50:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の239日目です。

Allegro CL: Flavorsとはなにか

 Allegro CL: Flavorsは、Allegro CLのFlavorsの実装です。

パッケージ情報

パッケージ名Allegro CL: Flavors
ドキュメントFlavors
Quicklisp×

インストール方法

 Allegro CL上で

(require :flavors)

とすれば利用可能です。

試してみる

 毎度お馴染のBankAccountです。

 Spice LispのFlavorsと全く同じコードも動きますが、(send self ...)が(send-self ...)と書けたりするので、そちらを使ってみています。

(require :flavors)


(defpackage :allegroflavors.demo
  (:use :cl :flavors)
  (:shadowing-import-from :flavors
                          :defmethod :make-instance))


(cl:in-package :allegroflavors.demo)


(defflavor bank-account ((dollars 0)) ()
  :initable-instance-variables
  :gettable-instance-variables
  :settable-instance-variables)


(defmethod (bank-account :deposit) (x)
  (send-self :set-dollars (+ (send self :dollars) x)))


(defmethod (bank-account :withdraw) (x)
  (send-self :set-dollars (max 0 (- (send self :dollars) x))))


(defparameter *my-account* (make-instance 'bank-account :dollars 200))


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

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

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

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


(defflavor stock-account ((num-shares 0) (price-per-share 30))
                         (bank-account)
  :initable-instance-variables
  :gettable-instance-variables
  :settable-instance-variables)


(defmethod (stock-account :set-dollars) (x)
  (setq num-shares (/ x price-per-share))
  (send-self :dollars))


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


(defparameter *my-stock* (make-instance 'stock-account :num-shares 10))


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


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


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


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


(send *my-stock* :withdraw 120)
;=> 540


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

まとめ

 今回は、Allegro CL: Flavorsを紹介してみました。
Allegro CLでは、過去のコードとの互換性の為に用意しているようですが、それもなかなか凄いような気がします。
Flavorsも実装が色々あり、Flanz LispもFlavorsはMITのLispマシンからの移植版が利用できたようです。もしかしたら、それが受け継がれているのかなと思い、パッケージ内のシンボルを眺めてみましたが、どうも結構MITのFlavorsとは違っているようなので、Allegro CLでは独自の実装となったのかもしれません。

ObjectLISPの紹介

Posted 2014-08-26 10:30:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の238日目です。

ObjectLISPとはなにか

 ObjectLISPは、Gary L. Drescher氏作の1984、5年頃に開発された今でいうプロトタイプベースのオブジェクト指向システムです。

パッケージ情報

パッケージ名ObjectLISP
Quicklisp×

インストール方法

にソースが転がっているので適当に移植します。
compiler-letと、Lispオブジェクトのアドレスを取得する関数が処理系にあれば、恐らく簡単に移植できると思います。
SBCLなら

(defun %pointer (obj)
  (sb-kernel:get-lisp-obj-address obj))

のようなところ。

試してみる

 ObjectLISPのマニュアルもネットに転がっていますが、知っている所は残念ながら現在リンク切れ。
代りに1985年頃にCommon Lispのオブジェクト指向システムを議論するメーリングリストにマニュアルのTeX原稿が流れていたので紹介してみます〈今のlatex等でも処理できます〉。

 ObjectLISPで良く知られている話としては、Common Lispに搭載されるオブジェクト指向システムが議論された時に、(New)Flavors、CommonLoops、CommonObjectsとObjectLISPで争った的な話がありますが、自分が眺める限りでは、ObjectLISP/LMIの人達にそんなにプッシュする気はなかったような気がしています。
ちなみに、やる気満々のXerox方面では、CommonLoopsのMOPを活用してObjectLISPを実装したとのこと。
また、ObjectLISPは、1986年位のMac用の処理系であるCorel CL=Macintosh Allegro CLでは、オブジェクト指向システムとして採用されていて、GUI関係での記述で専ら使われていた様子

 ということで毎度お馴染BankAccountを書いてみます。

プロトタイプベースなのでdefkindのようなものを使わなくてもmake-objしたものをoneof/kindofでコピーして作っていけますが、一応用意されているので下記の例では使ってみました。
特徴的な構文としては、askがありますが、これは、オブジェクトのボディ内で式が評価できる感じのものです。askのボディ内とdefobfunのボディ内ではインスタンス変数が取得できるのですが、元ネタがメソッド(dollars/set-dollars)をオーバーライドさせるのが肝みたいなので下記のコードではメソッドにしています。

(defpackage :objectlisp.demo
  (:use :cl :obj)
  (:shadowing-import-from :obj
                          . #.(intersection (*:list-external-symbols :obj)
                                            (*:list-external-symbols :cl)
                                            :test #'string=)))


(cl:in-package :objectlisp.demo)


(defkind =bank-account=)


(defobfun (exist =bank-account=) (&key* (dollars 0))
  (have 'dollars dollars))


(defobfun (dollars =bank-account=) ()
  dollars)


(defobfun (set-dollars =bank-account=) (x)
  (setq dollars x))


(defobfun (deposit =bank-account=) (x)
  (set-dollars (+ (dollars) x)))


(defobfun (withdraw =bank-account=) (x)
  (set-dollars (max 0 (- (dollars) x))))


(defparameter *my-account* (oneof =bank-account= 'dollars 200))


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


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


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


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


(defkind =stock-account= =bank-account=)


(defobfun (exist =stock-account=) (&key* (num-shares 0) (price-per-share 30))
  (have 'num-shares num-shares)
  (have 'price-per-share price-per-share))


(defobfun (set-dollars =stock-account=) (x)
  (setq num-shares (/ x price-per-share))
  (dollars))


(defobfun (dollars =stock-account=) ()
  (* num-shares price-per-share))


(defparameter *my-stock* (oneof =stock-account= 'num-shares 10))


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


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


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


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


(ask *my-stock*
  (withdraw 120))
;=>  540


(ask *my-stock*
  num-shares)
;=>  18

まとめ

 今回は、ObjectLISPを紹介してみました。
最初のプロトタイプベースの言語と言われるSelfが1980年代中期に誕生ということなので、ObjectLISPも最初期のものの一つなのではないでしょうか。

Spice Lisp: Flavorsの紹介

Posted 2014-08-24 15:50:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の237日目です。

Spice Lisp: Flavorsとはなにか

 Spice Lisp: Flavorsは、Steven Handerson氏作のSpice Lisp上のFlavorsの実装です。

パッケージ情報

パッケージ名Spice Lisp: Flavors
Quicklisp×

インストール方法

 Rutgers Common Lisp(TOPS-20 Common Lisp)のアーカイブの中にSpice Lispプロジェクトが配布していたFlavorsのファイルが埋もれていますので、適当に動かします。

 例のごとく、ANSI CLで動くようにしてみたものがありますので、良かったらどうぞ。

Quicklispのlocal-projectsに設置でもすれば、

(ql:quickload :spiceflavors)

で読み込めます。

FlavorsとCommon Lispについて

 現在のANSI Common Lispには、CLOSが含まれていることと、CLtL1の時代にはオブジェクト指向システムが含まれていなかったことから、CLOS以前にはオブジェクト指向システムはCommon Lispに搭載されていなかったと思う人が多いですが、実際のところは、1984〜6年当時のメジャーな処理系はCommon Lisp+Flavorsで処理系を配布/販売していて、Flavorsはほぼデファクトな存在でした。
主なところでは、

  • Spice Lisp
  • NIL
  • Lucid Common Lisp
  • VAX LISP
  • Allegro Common Lisp(の前身のExCL)
  • Symbolics
  • LMI

等です。ほぼCLtL1部分だけのKCLと、オブジェクト指向システムにはObject LISPを添付していたMCLにはFlavorsは付いてこなかった様子。ちなみにObject LISPの元祖はLMIなのでLMIにも含まれています。

試してみる

 今回もオブジェクト指向システムの紹介の度に書いているBankAccountというのを書いてみます。

 FlavorsとCLOSの主な違いといえば、どちらも多重継承ですが、Flavorsはマルチメソッドではありません。
また、Smalltalkのようなメッセージ送信が基本です。
さらに、Flavorsでは、インスタンス変数はメソッドのボディのスコープ中から参照できます。
今回のBankAccountでは、dollarsはメソッドがオーバーライドされるのでインスタンス変数としてアクセスしていませんが、price-per-share/num-sharesはインスタンス変数としてアクセスしてみています。

(defpackage :spiceflavors.demo
  (:use :cl :spiceflavors)
  (:shadowing-import-from :spiceflavors
                          . #.(intersection (*:list-external-symbols :spiceflavors)
                                            (*:list-external-symbols :cl)
                                            :test #'string=)))


(cl:in-package :spiceflavors.demo)


(defflavor bank-account ((dollars 0)) ()
  :initable-instance-variables
  :gettable-instance-variables
  :settable-instance-variables)


(defmethod (bank-account :deposit) (x)
  (send self :set-dollars (+ (send self :dollars) x)))


(defmethod (bank-account :withdraw) (x)
  (send self :set-dollars (max 0 (- (send self :dollars) x))))


(defparameter *my-account* (make-instance 'bank-account :dollars 200))


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

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

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

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


(defflavor stock-account ((num-shares 0) (price-per-share 30))
                         (bank-account)
  :initable-instance-variables
  :gettable-instance-variables
  :settable-instance-variables)


(defmethod (stock-account :set-dollars) (x)
  (setq num-shares (/ x price-per-share))
  (send self :dollars))


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


(defparameter *my-stock* (make-instance 'stock-account :num-shares 10))


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


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


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


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


(send *my-stock* :withdraw 120)
;=> 540


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

この例を書いていて判明しましたが、インスタンス変数の初期値を継承してこないバグがあるようです…。
移植によるものなのか、そもそもこういう仕様なのかは調査してみたいと思いますが、Allegro CLに付属してくるFlavorsと、VAX LISPに付属のFlavorsでは初期値は継承してくるようです。
ちなみに、上記では、stock-accountのdollarsは、bank-accountのdollars変数にアクセスしないため問題なく動きます。

まとめ

 今回は、Spice Lisp: Flavorsを紹介してみました。
今となっては伝説のFlavorsですが、実装は結構あるようです。
手軽に試すとすれば、Allegro CLで(require :flavors)するのが一番かもしれません〈最新の9.0の試用版でも可能〉。

html-parserの紹介

Posted 2014-08-23 15:40:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の236日目です。

html-parserとはなにか

 html-parserは、Alex Shinn氏によるChicken用のHTMLのパーザです。HTMLをsxmlに変換することも可能。

パッケージ情報

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

インストール方法

$ sudo chicken-install html-parser

すれば、

(use html-parser)

(require-extension html-parser)

試してみる

 html-parserは、Oleg Kiselyov氏のSSAXパーザのような、きっちりとしていて拡張性のあるインターフェイスでありつつ、壊れていたりすることの多いウェブのHTMLも簡便に扱えることを目標に作られたHTMLパーザとのこと。
用意されているのは下記のような関数です。

  • make-html-parser
  • html->sxml
  • sxml->html
  • sxml-display-as-html
  • html-display-escaped-string
  • html-strip
  • html-tag->string
  • html-escape
  • make-string-reader/ci
  • html-attr->string

 こんな感じの一行野郎も簡単に書けます〈(use http-client html-parser sxpath sxpath-lolevel)した状態で〉。

(for-each (compose print sxml:string-value) ((sxpath "//li/a") (html->sxml (with-input-from-request "http://tips.cddddr.org/scheme/index.cgi?c=a" #f read-string))))
;>> CGI を作る
;>> CPUのエンディアンを調べる
;>> CPUのワードサイズを調べる
;>> HTML を処理する
;>> InterWikiName
;>> R5RS ライブラリを R6RS ライブラリに加工する
;>> R6RS と SRFI で非互換な点
;>> XML を処理する
;>> XPath を使う
;>> anaphoric if
;>> quasisyntax を使う
;>> ...
               

一応の解説ですが、逆引きSchemeの項目一覧ページからli要素を抜き出す、という処理です。

まとめ

 今回は、html-parserを紹介してみました。
ちょっとしたテキスト処理は一行野郎で頑張ることも多いと思いますが、ちょっと込み入った処理になると、Common LispやSchemeで書いた方が早かったりしますよね。

zsortの紹介

Posted 2014-08-22 14:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の234日目です。

zsortとはなにか

 zsortは、Jorge Tavares氏作のCommon Lispのシークエンス用のソートライブラリです。

パッケージ情報

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

インストール方法

(ql:quickload :zsort)

試してみる

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

 Common Lispには標準でsortとstable-sortが用意されていますが、どんなソートアルゴリズムかを選択することはできないので、色々なアルゴリズムのソートを用意してみました、というライブラリです。
現在のところ

  • counting-sort
  • insertion-sort
  • heapsort
  • merge-sort
  • quicksort
  • randomized-quicksort

が用意されています。
動作は下記のような感じです。

(let ((u (*:iota 100 100 -1)))
  (zsort:counting-sort u))
;=>  (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
;     30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
;     56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
;     82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)


(let ((u (*:iota 100 100 -1)))
  (zsort:heapsort u #'<))
;=>  (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
;     30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
;     56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
;     82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)


(let ((u (*:iota 100 100 -1)))
  (zsort:insertion-sort u #'<))
;=>  (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
;     30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
;     56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
;     82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)


(let ((u (coerce (*:iota 100 100 -1) 'vector)))
  (zsort:merge-sort u #'<))
;=>  #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
;      30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
;      56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
;      82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)


(let ((u (*:iota 100 100 -1)))
  (zsort:quicksort u #'<))
;=>  (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
;     30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
;     56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
;     82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)


(let ((u (*:iota 100 100 -1)))
  (zsort:randomized-quicksort u #'<))
;=>  (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
;     30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
;     56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
;     82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)

ちなみに、どういう訳かmerge-sortがvectorにしか対応してないみたいです。

まとめ

 今回は、zsortを紹介してみました。
色々なアルゴリズムが選択できるというのは、なかなか良いですね。

Older entries (1760 remaining)