Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2010-05-31

(21)高速Common Lisp - HiLISPの実現(1986)

| 06:51 | (21)高速Common Lisp - HiLISPの実現(1986) - わだばLisperになる を含むブックマーク はてなブックマーク - (21)高速Common Lisp - HiLISPの実現(1986) - わだばLisperになる

まったりとCiNiiのLISP関係の論文を漁っておりますが、今回は、

です。

HiLISPは、日立のメインフレームHitac Mシリーズ上で動いていたCL処理系です。

日立もCLの処理系を作っていたなんて意外ですが、80年代後半には色々なところがCL処理系を作成していたようで、本文は、その実装方法を紹介しています。

HiLISPは、コンパイルされた関数の高速実行を目指しつつ、インタプリタの性能も犠牲にしない、という方針だったようです。

最近は割と、SBCLや、Clozure CLのようなコンパイラ指向の処理系が目立つのでインタプリタの速度を気にしているという話はあまり聞かない気がするのですが、当時はインタプリタの速度も重要視して実装しているところが割と多い気がしています。

実装の技法として、

  1. 多値の処理方法
  2. インタプリタでの変数管理で用いるディープバインディングの高速化
  3. 無限エクステントと、間接ポインタの併用

が挙げられています。

2010-05-30

*LispのシミュレーターをSBCLで動くようにする

| 14:08 | *LispのシミュレーターをSBCLで動くようにする - わだばLisperになる を含むブックマーク はてなブックマーク - *LispのシミュレーターをSBCLで動くようにする - わだばLisperになる

@machidaさんと、@komagataさんの会社no titleが運営しているHelp me, hackers!というサービスがあります。

自由に管理したいタスクを登録して、さらには通りすがりのハッカーに辻解決してもらえるかもしれないという素敵なサービスです。

自分も、*LispのシミュレーターがSBCLで動かないのをずっと解決したいと思っていたので早速登録してみていました。

まさか興味を持ってくれる人などいないと思っていたのですが、@Yubeshiさんに興味をもっていただいたりして、これはちょっと頑張ってみようということで、また詳しくエラーを追い掛けてみることにしました。

*Lispシミュレーターはパブリックドメインなのでとりあえずgithubにソースを設置し、付属のビルドシステムが面倒なのでASDFに対応させました。

SBCLでのエラーですが、追い掛けてみたところ、SBCLでは、defstructを定義するマクロが色々型宣言を付けるのですが、それが、*Lispシミュレーター側の型宣言と一致していないため色々問題が発生している様子。

ということでSBCLでは、*Lispシミュレーター側の型宣言を削除したところ、とりあえず*Lispシミュレーターのチュートリアルを動かす範囲ではエラーがでなくなりました。

ASDFにも対応してみましたので、

(ASDF-INSTALL:INSTALL "http://github.com/g000001/Starlisp-simulator/tarball/master")

インストールできます。

御興味のあるかたは試してみて下さい!

なお、Help me, hackers!ですが、ハッカーに解決をお願いするのは元より気軽に自分のタスク管理にも使えるとのことです。素敵ですね!

Getting Started in *LISP (24)

| 13:38 | Getting Started in *LISP (24) - わだばLisperになる を含むブックマーク はてなブックマーク - Getting Started in *LISP (24) - わだばLisperになる

前回が2009年の4/26日だったので実に1年ぶりですが、*Lisp入門の24回目です。

このペースだと一体いつ終了するのか…。

VPセットの扱い方をなんかしていた気がしますが、すっかり忘却の彼方です。

3.7 Summary: *Lisp as a Language

3章の振り返りです。

  • *whenや、if!!のような選択したプロセッサを利用する方法。
  • *psetや、*newのようなプロセッサ間でのデータのやりとりの方法
  • *sumやarray-to-pvarのようなフロントエンドとConnection Machineとのデータのやりとりの方法
  • scan!!や、spread!!、sort!!のようなデータの変形の方法
  • *cold-boot、def-vp-set、with-vp-setのような、プロセッサグリッドの操作方法

これらが*Lispの並列プログラミングツールとして主なところ。

次章の4章からはエラーハンドリングとデバッガの使用方法について説明するとのこと

ということで、次回、4から再開ですが、上記の主な操作方法のことは、すっかり忘れています…。

2010-05-29

KMRCLを眺める(158) IF*

| 18:41 | KMRCLを眺める(158) IF* - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(158) IF* - わだばLisperになる

web-utils.lispも眺め終わったので、次のファイルということで、今回は、KMRCLのifstar.lispからIF*です。

if*は、KMRCLで定義されているというよりは、Franz社がパブリックドメインで公開しているもので、KMRCL内でもexportはされていません。

kmr氏はAllegro CLを良く使っているのか、Allegro向けの定義が良く見掛けられます。

if*なんて邪道、という意見も聞いたことがあるのですが、if*は、FranzLispのifに由来するようなので、Franz社が独自にCLを拡張しようとしているというより社内の伝統なのかもしれません。

(ちなみにFranzLispでは大文字と小文字を区別するので、大文字と小文字を区別するAllegro CLのmlispも、また伝統というかこだわりなのかもしれません)

オリジナルのFranzLispの説明では、

;--- if :: macro for doing conditionalization
;
;  This macro is compatible with both the crufty mit-version and
; the keyword version at ucb.
;
;  simple summary:
;   non-keyword use:
;	(if a b) ==> (cond (a b))
;	(if a b c d e ...) ==> (cond (a b) (t c d e ...))
;   with keywords:
;	(if a then b) ==> (cond (a b))
;	(if a thenret) ==> (cond (a))
;	(if a then b c d e) ==> (cond (a b c d e))
;	(if a then b c  else d) ==> (cond (a b c) (t d))
;	(if a then b c  elseif d  thenret  else g)
;		==> (cond (a b c) (d) (t g))

となっていてIF一つでMIT方式とFranz(UCB)方式の両方の書式に対応できたようです。

使われ方としては、

(IMPORT 'KL::IF*)
(DEFUN S= (S1 S2)
  (AND (STRING= S1 S2) S1))

(let ((s "3"))
  (if* (s= "1" s)
      thenret
   elseif (s= "2" s)
      thenret
   elseif (s= "3" s)
      thenret))
;⇒ "3"

(let ((s "3"))
  (if* (string= "1" s)
      then 1
   elseif (string= "2" s)
      then 2
   elseif (string= "3" s)
      then 3))
;⇒ 3
(let ((s "3"))
  (if* (s= "1" s)
      :thenret
   :elseif (s= "2" s)
      :thenret
   :elseif (s= "3" s)
      :thenret))
;⇒ "3"

というところでしょうか。

IF*はCONDに展開されるのですが、CONDの述語部での返り値を利用するTHENRETが使が使えます。

CONDだとスタイル上あまりこの値を利用するのは好ましくないようですが、THENRETと名前が付けば割と見通しも良いのでたまに便利に使えそうでもあります。

ただCONDの述語部では、返り値は多値で返らないので、その辺りに留意する必要がありそうです。

折角なので、インデントにもこだわって行きたいところですが、FranzLispのソースを眺めると

  1. thenや、elseは行に単独で現われない
  2. thenや、else、thenretはif*の述語の一個前か、同じ位置から開始
  3. elseifはif*と同じ位置から開始

とすることが多いようです。

また、CL版は、文字列として比較しているので、キーワードでもOKです。

定義は、

;; the if* macro used in Allegro:
;;
;; This is in the public domain... please feel free to put this definition
;; in your code or distribute it with your version of lisp.

(in-package #:kmrcl)

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar if*-keyword-list '("then" "thenret" "else" "elseif")))

(defmacro if* (&rest args)
   (do ((xx (reverse args) (cdr xx))
        (state :init)
        (elseseen nil)
        (totalcol nil)
        (lookat nil nil)
        (col nil))
       ((null xx)
        (cond ((eq state :compl)
               `(cond ,@totalcol))
              (t (error "if*: illegal form ~s" args))))
       (cond ((and (symbolp (car xx))
                   (member (symbol-name (car xx))
                           if*-keyword-list
                           :test #'string-equal))
              (setq lookat (symbol-name (car xx)))))

       (cond ((eq state :init)
              (cond (lookat (cond ((string-equal lookat "thenret")
                                   (setq col nil
                                         state :then))
                                  (t (error
                                      "if*: bad keyword ~a" lookat))))
                    (t (setq state :col
                             col nil)
                       (push (car xx) col))))
             ((eq state :col)
              (cond (lookat
                     (cond ((string-equal lookat "else")
                            (cond (elseseen
                                   (error
                                    "if*: multiples elses")))
                            (setq elseseen t)
                            (setq state :init)
                            (push `(t ,@col) totalcol))
                           ((string-equal lookat "then")
                            (setq state :then))
                           (t (error "if*: bad keyword ~s"
                                              lookat))))
                    (t (push (car xx) col))))
             ((eq state :then)
              (cond (lookat
                     (error
                      "if*: keyword ~s at the wrong place " (car xx)))
                    (t (setq state :compl)
                       (push `(,(car xx) ,@col) totalcol))))
             ((eq state :compl)
              (cond ((not (string-equal lookat "elseif"))
                     (error "if*: missing elseif clause ")))
              (setq state :init)))))

となっています。

オリジナルのFranzLispifのコメント文によると4つの状態を持つシンプルなオートマトンになっているとのこと

最初に本体部がREVERSEされて渡されるのでわかりづらいですが、

  1. init: 完全なパーズ済みのボディか、then節を持った状態
  2. col: 次のif*のキーワードを待っている状態
  3. then: thenの直後で次にくる述語を待っている状態
  4. compl: thenの直後の述語をみた状態で、elseifか終了を待っている状態

の4つの変数で表わされているようです。

再帰で書いたら分かりやすくなるのかなと思い書き直しつつ、thenretで多値も扱えるようにしてみました

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar if**-keyword-list '("then" "thenret" "else" "elseif")))

(defmacro IF** (&body body)
  (multiple-value-bind (body thenret-vars)
      (parse-if :INIT (reverse body) nil () () () )
    (if thenret-vars
        `(LET ,thenret-vars
           (COND ,@body))
        `(COND ,@body))))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun parse-if (state args elseseen col totalcol thenret-vars)
    (if (endp args)
        (if (eq state :compl)
            (values totalcol thenret-vars)
            (error "if*: illegal form ~s" args))
        (let ((lookat (if (and (symbolp (car args))
                               (member (symbol-name (car args))
                                       if**-keyword-list
                                       :test #'string-equal))
                          (intern (symbol-name (car args)) :keyword)
                          :non-keyword))
              (xx (car args))
              (next (cdr args)))
          (case state
            (:INIT
             (case lookat 
               (:non-keyword 
                (parse-if :COL next elseseen (list xx) totalcol thenret-vars))
               (:thenret
                (let ((ret (gensym)))
                  (parse-if :THEN 
                            `((car (setq ,ret (multiple-value-list ,(car next))))
                              ,@(cdr next))
                            elseseen 
                            `((values-list ,ret))
                            totalcol
                            (cons ret thenret-vars))))
               (otherwise
                (error "if*: bad keyword ~a" lookat))))
            (:COL
             (case lookat
               (:else
                (when elseseen
                  (error "if*: multiples elses"))
                (parse-if :INIT 
                          next 'T col `((t ,@col) ,@totalcol) thenret-vars))
               (:non-keyword
                (parse-if :COL
                          next elseseen (cons xx col) totalcol thenret-vars))
               (:then
                (parse-if :THEN
                          next elseseen col totalcol thenret-vars))
               (otherwise (error "if*: bad keyword ~s" lookat))))
            (:THEN
             (case lookat
               (:NON-KEYWORD
                (parse-if :COMPL 
                          next elseseen xx `((,xx ,@col) ,@totalcol) thenret-vars))
               (otherwise 
                (error "if*: keyword ~s at the wrong place " xx))))
            (:COMPL
             (case lookat
               (:elseif 
                (parse-if :INIT
                          next elseseen col totalcol thenret-vars))
               (otherwise 
                (error "if*: missing elseif clause ")))))))))
(if** (values 42 nil)
     thenret
 elseif (values nil nil)
     thenret
     else 'foo)

のようなものは、

(LET (#:G2955 #:G2954)
  (COND
    ((CAR (SETQ #:G2955 (MULTIPLE-VALUE-LIST (VALUES 42 NIL))))
     (VALUES-LIST #:G2955))
    ((CAR (SETQ #:G2954 (MULTIPLE-VALUE-LIST (VALUES NIL NIL))))
     (VALUES-LIST #:G2954))
    (T 'FOO)))

と展開されます。

しかし、再帰で書きなおしたものの想像したよりも分かりやすくなってもおらず…。

2010-05-28

(20)TAOのパッケージシステム(1994)

| 07:59 | (20)TAOのパッケージシステム(1994) - わだばLisperになる を含むブックマーク はてなブックマーク - (20)TAOのパッケージシステム(1994) - わだばLisperになる

まったりとCiNiiのLISP関係の論文を漁っておりますが、今回は、

です。

本文のTAOは、ELIS上で稼動する、TAO/ELISではなくて、記号処理専用マシンのSILENT上で稼動するTAO/SILENTとのこと。

TAOはCommon Lispとは違ったパッケージシステムを採用していて

Common Lispとちがっているところで特徴的なところは、

  1. OSレベルでのシンボルとパッケージの保護
  2. package pathがあり、UNIXシェルのPATHのように複数pathを登録できて、先の方が優先される
  3. 外部シンボル、内部シンボルの違いはなく、":"で区切られるのみ("::"はない)
  4. システムやアプリが提供するシンボルはユーザーには変更不可なのでエイリアス機構を提供

TAO/ELISや、Lisp machine lispでは、パッケージが階層構造を取れたようなのですが、本文では、階層化に触れられていませんでした。TAO/SILENTはCLと同じように階層化されてなかったりするのでしょうか。

全然関係ないですが、本文中の「TAOは、SILENTの機械語」というのにグッと来ました

2010-05-27

(19)ELIS Common Lispのマルチプログラミング機能(1989)

| 13:24 | (19)ELIS Common Lispのマルチプログラミング機能(1989) - わだばLisperになる を含むブックマーク はてなブックマーク - (19)ELIS Common Lispのマルチプログラミング機能(1989) - わだばLisperになる

まったりとCiNiiのLISP関係の論文を漁っておりますが、今回は、

です。

国産のLISPマシンであるELISでは、TAOが動いていたというのは知られるところだと思うのですが、CLも動いていたというは、CiNiiで論文が読めるようになってから知りました。

ELIS Common Lispは、TAOの機構を継承し、CLと競合するところは、CLを優先、ということなので、TAOのCL版という感じだったのかもしれません。

本文は、ELIS CLのマルチプログラミング機能について述べられていますが、CLでは、マルチプログラミング機能は仕様に定められておらず(当時のCLtL1でも今のANSI CLでも)、拡張部分となります。

特徴としては、単一アドレス空間で、リエントラント性が確保されるような拡張がされていて、

  1. 名前空間は、プロセスごとにCLのパッケージを切る
  2. 同期プリミティブは、言語仕様には含めず、システムが提供するものを利用する
  3. トップレベルの変数もプロセスごとにLETでの束縛のように振る舞う(大域の共有はしない)。
    1. 共有する場合は、defglobalか、defconstant宣言する。defglobalで宣言すると束縛はできず、代入しかできない変数となる
  4. シンボルの属性リストはプロセスごとに異なるようにする。共通にしたい場合は、特別な宣言をする

等が実現方法として述べられています。

ここ1年位で、SBCLにも拡張としてdefglobalが導入されて、その性質も同じく代入しかできないというものですが、他のCL処理系の拡張や、MIT系のLispマシンにもあるようなので、この辺りは定番なのかもしれません。

プロセスごとにごとにパッケージを用意するというのは、自動で行なわれるのか、ユーザーがそういうコーディングをするものなのか詳しく述べられてはいませんが、興味深いです。

ちなみに、SBCLのスレッドでは、

  1. 大域のスペシャル変数は、全スレッドで共有される
  2. (ローカル)束縛のスペシャル変数は、親子スレッドで共有されない
  3. (ローカル)レキシカル変数は、親子スレッドで共有される

となっているようです。最近のCL処理系は大体こういう動きみたいですが、振舞い的にはELIS CLと結構違いますね。

2010-05-26

KMRCLを眺める(157) SPLIT-URI-QUERY-STRING

| 08:00 | KMRCLを眺める(157) SPLIT-URI-QUERY-STRING - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(157) SPLIT-URI-QUERY-STRING - わだばLisperになる

今回は、KMRCLのweb-utils.lispからSPLIT-URI-QUERY-STRINGです。

foo=1&bar=2&baz=3のようなクエリパラメータをalistにして返す関数のようで動作は、

(KL:SPLIT-URI-QUERY-STRING "foo=1&bar=2&baz=3")
;⇒ (("foo" . "1") ("bar" . "2") ("baz" . "3"))

のような感じです。

定義は、

(defun split-uri-query-string (s)
  (mapcar
   (lambda (pair)
     (let ((pos (position #\= pair)))
       (when pos
         (cons (subseq pair 0 pos)
               (when (> (length pair) pos)
                 (decode-uri-query-string (subseq pair (1+ pos))))))))
   (delimited-string-to-list s #\&)))

となっていて、

(KL:DELIMITED-STRING-TO-LIST "foo=1&bar=2&baz=3" #\&)
;⇒ ("foo=1" "bar=2" "baz=3")

して、POSITIONで#\=の位置を割り出し、SUBSEQで前半と後半を切り出しつつDECODE-URI-QUERY-STRINGもするというところです。

2010-05-25

(18)CommonLoops: Common Lispオブジェクト指向機能の標準化原案(1986)

| 08:01 | (18)CommonLoops: Common Lispオブジェクト指向機能の標準化原案(1986) - わだばLisperになる を含むブックマーク はてなブックマーク - (18)CommonLoops: Common Lispオブジェクト指向機能の標準化原案(1986) - わだばLisperになる

まったりとCiNiiのLISP関係の論文を漁っておりますが、今回は、

です。

CommonLoopsとは、COMMON Lisp Object Oriented Programming Systemの略で、Interlisp上のLOOPSから発展しているものの様子。

CLにオブジェクト指向の標準としてCLOSが組み込まれる際に色々なシステムが提案されたのですが、その中でも標準的な核の案になったもののようです。

当時提案されたシステムのの中で有力なものは、CommonLoops以外には、ObjectLISP(LMI提案/今でいうプロトタイプ指向)、CommonObject(HP提案/オブジェクトの隠蔽方法に特徴があるらしい)、newFlavors(Symbolics提案/Flavorsの発展したもの)があったようなのですが、CommonLoopsは設計の良さと、他のシステムの提案した機能をCommonLoops上に組み込めることを実証したことが主流になっていった理由のようです。

なんとなくAMOPの内容(本文/練習問題など)はこの辺りの流れからから来ているのかなとちょっと思いました。

本文では、CommonLoopsの実現方法と、特徴について説明があり、CLOS系のオブジェクト指向を理解するにもちょうど良い資料かなと思いました

後半に少し触れられていますが、Prologとの融合も考えていた人がいたというのも興味深いです。

TAOでも、SmalltalkとPrologの融合がはかられていますが、80年代末期には面白いものが多そうです。

2010-05-24

KMRCLを眺める(156) DECODE-URI-QUERY-STRING

| 14:09 | KMRCLを眺める(156) DECODE-URI-QUERY-STRING - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(156) DECODE-URI-QUERY-STRING - わだばLisperになる

今回は、KMRCLのweb-utils.lispからDECODE-URI-QUERY-STRINGです。

エンコードされたURIをデコードするというもので以前眺めた、strings.lispのDECODE-URI-STRINGと殆ど同じですが、クエリ部分を想定しているので+の解釈が違っています。

(SET' URI "foo%2Fbar%2Fbaz+quux")

(KL:DECODE-URI-STRING URI)
⇒ "foo/bar/baz+quux"

(KL:DECODE-URI-QUERY-STRING URI)
⇒ "foo/bar/baz quux"

定義は、

(defun decode-uri-query-string (s)
  "Decode a URI query string field"
  (declare (simple-string s)
           (optimize (speed 3) (safety 0) (space 0)))
  (do* ((old-len (length s))
        (new-len (- old-len (* 2 (the fixnum (count-string-char s #\%)))))
        (new (make-string new-len))
        (p-old 0)
        (p-new 0 (1+ p-new)))
       ((= p-new new-len) new)
    (declare (simple-string new)
             (fixnum p-old p-new old-len new-len))
         (let ((c (schar s p-old)))
           (when (char= c #\+)
             (setq c #\space))
           (case c
             (#\%
              (unless (>= old-len (+ p-old 3))
                (error "#\% not followed by enough characters"))
              (setf (schar new p-new)
                    (code-char
                     (parse-integer (subseq s (1+ p-old) (+ p-old 3))
                                    :radix 16)))
              (incf p-old 3))
             (t
              (setf (schar new p-new) c)
              (incf p-old))))))

となっていますが、これも大体DECODE-URI-STRINGと同じです。

2010-05-23

みんなでLISPのウェブアプリを作る場所が欲しい

| 19:33 | みんなでLISPのウェブアプリを作る場所が欲しい - わだばLisperになる を含むブックマーク はてなブックマーク - みんなでLISPのウェブアプリを作る場所が欲しい - わだばLisperになる

phpやperl、ruby等は気軽に個人で廉価なレンタルサーバーを借りてウェブアプリを作成して楽しむことができると思うのですが、Common Lispは、スクリプト言語的な使われ方は苦手で、大抵の廉価なサーバーでは満足に動かせないか、作れても遅かったりしてのびのびとLISP的に書けないことが多いのではないかと思います。

実際、Common Lispでは、サーバーからCommon Lispで組んでいくというスタイルが多いようで、CLのウェブサーバーもありますし、フレームワークも幾つかあります。

そうなってくるとVPSや専用サーバーか、ということになるのですが、月額7、8千円で、安いところで3千円位からなので、ちょっと試して上手く行かなくて放置しておくには、年間3〜10万は、微妙な金額です。

一人でぐるぐる悩むよりみんなで割り勘したら問題解決か!ということで、みんなでLISPのウェブアプリを作る場所が欲しい人を募集してみたいと思います。

LISPで作るというのが目的なので、作るものは問いません。

私個人としては、CL版「コインランドリー刑事(デカ)」のようなものを作ってみたいと考えています。(もしくは、いまさらのcommon-lisp-users.jpとか)

サーバーの運営費を払ってでも、

  1. LISPで共同開発の経験をしてみたい
  2. 仕事のプロジェクトでCommon Lispを採用するのもなかなか色々難しいけど何か作る場所は欲しい
  3. Weblocks/Kahuaを動かしてなんかしてみたい
  4. LISPのウェブアプリを作るってどんなことになるのかに興味がある
  5. LISPでウェブアプリを作ったりしてみたいが、そんなに気合いも入ってないし、途中で飽きそうだし、安上りにしておきたい
  6. もう専用サーバーを借りてウェブアプリも動かしてみたけどサーバーがオーバースペックだし維持費が安いところに引っ越したい
  7. ウェブデザイナーだけどLisperと組んで仕事をしてみたい(お金は稼げません)
  8. 企画/営業だけどLisperと組んで仕事をしてみたい(お金は稼げません)

という方は、ここにコメントを頂けるか、Twitterで @g177564 に@して頂けると嬉しいです(Twitterはあまりチェックしていないので返事が遅れるかもしれません)

ちなみに、Lisperと組んで仕事をしてみたいデザイナーさんについては、一人見かけたことがあるので声を掛けてみたいです。

2010-05-22

(17)Tachyon Common LispのSPARCへの移植(1993)

| 12:18 | (17)Tachyon Common LispのSPARCへの移植(1993) - わだばLisperになる を含むブックマーク はてなブックマーク - (17)Tachyon Common LispのSPARCへの移植(1993) - わだばLisperになる

まったりとCiNiiのLISP関係の論文を漁っておりますが、今回は、

です。

以前、PA-RISCへの移植の論文を紹介しましたが、こちらはSPARC版です。

しかし、時系列としては、もともと、i860で稼動していたものが、SPARC、PA-RISCの順で移植されていった様子。

当時のTachyon CLは、CLtL2に基いています。

PA-RISCの時は、15万行の処理系のプログラムを3.25人月で移植できたそうですが、SPARCへの場合では、2.5人月だったようです。

i860からSPARCに移植して性能の向上が見られたとのことで、移植性の高さが実証できたとしています。

今からすると、Tachyon CLがx86系に移植されないのは何故なのかというところですが、当時、ワークステーションといえば、SPARC、MIPS、Alpha等のRISCの勢いがあり性能も高く、PC-UNIXが流行するのもあと3、4年後といったところなので移植されないのも当然といえば当然なのかもしれません。

2010-05-21

KMRCLを眺める(155) MAKE-URL

| 18:39 | KMRCLを眺める(155) MAKE-URL - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(155) MAKE-URL - わだばLisperになる

今回は、KMRCLのweb-utils.lispからMAKE-URLです。

名前のとおりURLを生成するのに利用します。

使い方は、

(KL:MAKE-URL "page" 
             :FORMAT :HTML
             :VARS '(("foo" . "1")
                     ("bar" . "2"))
             :ANCHOR "baz")
;⇒ "http://example.com/page?foo=1&bar=2#baz"

という風に、パラメータをalist、アンカを文字列で渡せます

定義は、

(defun make-url (page-name &key (base-dir *base-url*) (format :html) vars anchor)
  (let ((amp (case format
               (:html
                "&")
               ((:xml :ie-xml)
                "&"))))
    (concatenate 'string
      base-dir page-name
      (if vars
          (let ((first-var (first vars)))
            (concatenate 'string
              "?"  (car first-var) "=" (cdr first-var)
              (mapcar-append-string
               #'(lambda (var)
                   (when (and (car var) (cdr var))
                     (concatenate 'string
                       amp (string-downcase (car var)) "=" (cdr var))))
               (rest vars))))
        "")
      (if anchor
          (concatenate 'string "#" anchor)
        ""))))

MAPCAR-APPEND-STRINGは以前眺めたKMRCL内のlists.lispで定義されています。

2010-05-20

UranusをANSI Common Lispで動かそう

| 13:10 | UranusをANSI Common Lispで動かそう - わだばLisperになる を含むブックマーク はてなブックマーク - UranusをANSI Common Lispで動かそう - わだばLisperになる

最近ちょっとPrologに手を出し始めたのですが、UranusというLISP上で動く処理系が公開されていたことを思い出しました。

Uranus Prolog/KR

こちらのページでは、オリジナルをGCLに対応させています。

このページによると、CMUのAIレポジトリでソースが公開されているようなので、

このソースをASDFに対応させてセットアップが楽にできるようにしてみました。

SBCLで作業してみたのですが、Allegro CL、Clozure CL、XCLでも動くことを確認しました。しかし、若干微妙なところがありますので、今後ちょこちょこ直して行きたいです。

主な変更は下記のとおりです。

uranus.asd

Zetalisp用のdefsystemの定義があったりしてファイルの読み込み順の参考にできそうですが、とりあえず、上から順に読み込むasdファイルを作成しました。

(in-package :cl-user)

(asdf:defsystem :URANUS
  :name "Uranus"
  :description "Uranus is an extension of Prolog written in Common Lisp and has the syntax of Lisp."
  :version "V-19.8"
  :serial t
  :components ((:file "package")
               (:file "decl")
               (:file "defs")
               (:file "lib")
               (:file "kernel")  
               (:file "amuse")
               (:file "systempred")
               (:file "lispfunctions") 
               (:file "stepper")
               (:file "trace")
               (:file "td")
               (:file "readin")))

package.lisp

パッケージの指定がCLtL1なのでこれを変更するためにpackage.lispを作成しました

(DEFPACKAGE :URANUS
  (:USE :CL)
  (:NICKNAMES :URA)
  (:EXPORT :uranus@version))

(DEFPACKAGE :URANUS-USER
  (:USE :CL :CL-USER)
  (:NICKNAMES :URA-USER))

;;; Amuse package family: 
(defpackage Amuse (:use :cl :cl-user))
(defpackage ec (:use :amuse :cl :cl-user) (:nicknames :e))

(IN-PACKAGE :URANUS)
(defvar uranus@version "V-19.8")

他に修正するところ

CLtL1的なところからの変更
  1. 今のANSI CLでのCL-USERは以前USERと書けていていたので、これをCL-USERに変更(自分は、URANUS-USERパッケージを作成してみました)
  2. CLtL1では、in-packageしただけでパッケージが作成されたのですが、これを明示的にDEFPACKAGEで作成するように変更
  3. special-form-pは廃止なので、special-operator-pに変更(special-form-pのマクロを定義)
Zetalisp的なところからの変更
  1. memq等がないとなるので、lib.lispの#+kclの箇所を#+ansi-clに変更
  2. zl:sendがないといわれるので、#+lispm等を付けてスルー
他の変更
  1. パッケージロック等で警告があるので好きなように変更

できれば、asdf-installできると嬉しいので、githubに公開しようと思いましたが、ライセンスに再配布について明記されていないようなので見送っています。

動作

CL-USER> (URANUS:URANUS)
:
:(ASSERT (FOO 3))
:(foo *x)
(FOO 3)
:;assertを纒めたdefineというのがあるので試してみる
(DEFINE COMPRESS 
  ( ((*X) (*X)) )
  ( ((*X *X . *T) *ANS)
    (COMPRESS (*X . *T) *ANS) )
  ( ((*X *Y . *T) (*X . *ANS))
    (COMPRESS (*Y . *T) *ANS) ))
(DEFINE COMPRESS (((*X) (*X))) (((*X *X . *T) *ANS) (COMPRESS (*X . *T) *ANS)) (((*X *Y . *T) (*X . *ANS)) (COMPRESS (*Y . *T) *ANS)))
:(compress (4 4 0 0 1 1 1) *x)
(COMPRESS (4 4 0 0 1 1 1) (4 0 1))

Uranusには、Zmacs(Lispマシンのエディタ)と連携するための設定が付属してくるのですが、これを参考にSLIMEと連携させるようにしてみたいところです。

2010-05-19

(16)何故, LISPに基づいたコマンド言語がよいのか(1981)

| 07:59 | (16)何故, LISPに基づいたコマンド言語がよいのか(1981) - わだばLisperになる を含むブックマーク はてなブックマーク - (16)何故, LISPに基づいたコマンド言語がよいのか(1981) - わだばLisperになる

情報処理(学会誌)では、海外の文献を紹介するコーナーもあるようなのですが、海外のWhy a LISP-Based Command Language?(1980)とA LISP SHELLの紹介です。

当時、UNIXシェル等のコマンド言語の便利さが知られるようになって来ていたようですが、成熟した対話環境を持つLISPはコマンド言語の要求にも叶う、ということでLISPの中にUNIXシェルを埋め込んだ、LISP Shellの実現が報告されています。

現在、シェルとして拡張しているScheme処理系にscsh等がありますが30年位前からアイデアはあったようです。

Shibuya.lisp TT#4での近山先生のUtilispの講演でもUtilisp内からOSを操作できるようにして便利に使っていたというお話がありましたし、ITS上のMacLISPもOSの機能はLISP内から全部使えたようです。

また、当然ながらLISPマシンは上から下まで全部LISPですが、UNIX上でもそういう環境に近付けたい、ということだったのかもしれません。

LISP内から外に出ないでOSを操作できるというのは良いですね。

2010-05-18

KMRCLを眺める(154) BASE-URL!

| 21:36 | KMRCLを眺める(154) BASE-URL! - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(154) BASE-URL! - わだばLisperになる

今回は、KMRCLのweb-utils.lispからBASE-URL!です。

名前のとおりユーティリティで使うベースのURLを設定するもののようで、定義はずばり

;;; URL Functions
(defvar *base-url* "")
(defun base-url! (url)
  (setq *base-url* url))

となっています。

使い方は、

(KL:BASE-URL! "http://example.com/")

KL:*BASE-URL*
;=> "http://example.com/"

です。大域変数にアクセサを定義しているというところでしょうか。

(setq kl:*base-url* "http://example.com/")

よりは見通しが良さそうです。

2010-05-17

StumpWMの日々 (5) 〜オフライン時のスタートアップ〜

| 13:44 | StumpWMの日々 (5) 〜オフライン時のスタートアップ〜 - わだばLisperになる を含むブックマーク はてなブックマーク - StumpWMの日々 (5) 〜オフライン時のスタートアップ〜 - わだばLisperになる

最近自分はオフラインでPCを使うことが多いのですが、オフラインの時には、スタートアップ時にネット接続がが必須なアプリが起動してくるのがちょっと嫌でした。

stumpwm/contrib/net.lispにはデフォルトの接続デバイスを判定する関数があるのですが、これが、"lo"(ローカルループバック)を検出している時には、ネットに接続していないと言えるので、これを利用してみることにしました。

(LOAD "../stumpwm/contrib/net")

(WHEN (STRING/= "lo" (STUMPWM.CONTRIB.NET::NET-DEVICE))
  (SKYPE)
  (CHROME)
  (FIREFOX)
  (THUNDERBIRD) )

最近は常時ネットに接続している人がほとんどだと思うのであまり役に立たなそうですが、自分的にはかなり快適になりました :)

2010-05-16

(15)LISP(<大特集>プログラミング言語の最近の動向)(1981)

| 17:43 | (15)LISP(プログラミング言語の最近の動向)(1981) - わだばLisperになる を含むブックマーク はてなブックマーク - (15)LISP(プログラミング言語の最近の動向)(1981) - わだばLisperになる

CiNiiのLISP関係の論文をのんびり漁っております。今回は、

です。

もちろん、最近の動向といっても1981年当時のことで、LISPの初期の歴史の解説をさらっとした後で動向を解説しています

当時の最新動向としては、LISPマシンの台頭が挙げられ、

  1. 実時間GC
  2. ハードウェアサポートによるプログラム実行
  3. 並列処理
  4. ユーザーとの対話的操作でのS式以外の入力方法
  5. リスト以外のデータ構造や効率的な表現(CDR-Coding、ベクタ、タプル)

等が目新しいところだったようです。

また、ユーザー層の変化があったらしく、それまでLISPはAI研究者が使うものだったのが、アプリの利用者や、当時のマイコンユーザーにも広がりつつあったようです。

加えて、これも当時台頭しつつあったCommon LispがLISP規格化の噂という形で話題になっています。

この記事中では、規格化には否定的な人が多いとして「規格化は言語の発達を阻害する」「規格化は不可能である」「規格化は不必要である」等の例を挙げていますが、それぞれ面白いなと思いました。

2010-05-15

KMRCLを眺める(153) USER-AGENT-IE-P

| 17:09 | KMRCLを眺める(153) USER-AGENT-IE-P - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(153) USER-AGENT-IE-P - わだばLisperになる

今回は、KMRCLのweb-utils.lispからUSER-AGENT-IE-Pです。

名前のとおりユーザーエージェントがIEかどうかを判定するもののようで定義は、

;;; User agent functions

(defun user-agent-ie-p (agent)
  "Takes a user-agent string and returns T for Internet Explorer."
  (or (string-starts-with "Microsoft" agent)
      (string-starts-with "Internet Explore" agent)
      (search "Safari" agent)
      (search "MSIE" agent)))

となっていますが、なぜSafafiを判定しているのかは謎です。そういうものなのでしょうか。

動作例は、
(KL:USER-AGENT-IE-P "127.0.0.1 - - [15/May/2010:15:48:22 +0900] \"GET / HTTP/1.0\" 200 359 \"-\" \"Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)")
;=> 98

となっていて、98が帰ってくると若干混乱しますが、上記の例の場合、SEARCHの結果がそのまま帰ってきているのでこうなっています。

また、KMRCL内で定義したstring-starts-withも利用されています。

2010-05-14

(14)浅い束縛による動的スコープ変数が存在する時の末尾再帰呼び出し(2000)

| 19:17 | (14)浅い束縛による動的スコープ変数が存在する時の末尾再帰呼び出し(2000) - わだばLisperになる を含むブックマーク はてなブックマーク - (14)浅い束縛による動的スコープ変数が存在する時の末尾再帰呼び出し(2000) - わだばLisperになる

CiNiiのLISP関係の論文をのんびり漁っております。今回は、

です。

レキシカルスコープのLISPでは末尾再帰がGotoに変換される(末尾再帰の除去)ものがあり、Schemeのように言語の仕様として必須だったりするものもありますが、ダイナミックなスコープのLISPで末尾再帰の最適化を行なうというのはあまり聞いたことがありませんでした。

ダイナミックスコープで深い束縛を採用した場合には、末尾再帰の除去をする方法は知られているそうですが、この論文では、浅い束縛で末尾再帰の除去をする方法(Emacs Lisp)、レキシカルスコープとダイナミックスコープの両方がある場合での除去(Common Lisp)を示し、実際に、Emacsを改造し、Emacs Lispに真の末尾再帰呼び出しを実装して性能を測定しています。

論文の後半でも述べられていますが、末尾再帰が最適化されるとEmacs Lispの記述スタイルも大分変わりそうですね。

2010-05-13

KMRCLを眺める(152) HTML/XML constants

| 13:56 | KMRCLを眺める(152) HTML/XML constants  - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(152) HTML/XML constants  - わだばLisperになる

strings.lispは眺め終わったので次はどれにしようかというところですが、手軽なところで、web-utils.lispにしてみることにしました。

ウェブプログラミングで使うようなちょっとしたユーティリティといったところです。

ということで、今回は、KMRCLのweb-utils.lispからHTML/XML constantsです。

HTML/XMLで良く使いそうな、定数を定義しています。

;;; HTML/XML constants

(defvar *standard-xml-header*
  #.(format nil "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"yes\"?>~%"))

(defvar *standard-html-header* "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">")

(defvar *standard-xhtml-header*
  #.(format nil "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"yes\"?>~%<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">"))

リード時に評価して文字列にしているのは分かるのですが、FORMAT式の中身に評価して展開されるものがないので、ちょっと不思議です。

展開されるものといえば、Newline(~%)位ですが、これが状況によって違ってくる可能性があるからでしょうか。

もしくは、ファイル中の改行を問題と考えているのか…。

2010-05-12

SLIMEとLTDを使ってCLのコードをDylanに変換して表示する

| 19:06 | SLIMEとLTDを使ってCLのコードをDylanに変換して表示する - わだばLisperになる を含むブックマーク はてなブックマーク - SLIMEとLTDを使ってCLのコードをDylanに変換して表示する - わだばLisperになる

まったくもって誰も興味がなさそうなエントリーですが、LTDとSLIMEを組み合わせてみたら面白いかなと思い、組み合わせて遊んでみました。

LTDは、最近翻訳された「実用Common Lisp」の著者であるPeter Norvig氏が作成したCommon LispをDylanに変換するツールです。

SLIME

(defun random-string (&key (length 10) (set :lower-alpha))
  "Returns a random lower-case string."
  (declare (optimize (speed 3)))
  (let ((s (make-string length)))
    (declare (simple-string s))
    (dotimes (i length s)
      (setf (schar s i) (random-char set)))))

のようなCLのコード上でslime-ltdすると

// LtD

define method random-string (#key length = 10, set = #"lower-alpha")
  // Returns a random lower-case string.
  let s :: <simple-string> = make(<string>, size: length, fill: ' ');
  for (i from 0 below length) s[i] := random-char(set); finally s; end for;
end method random-string;

のように別枠のバッファにDylanに変換され表示されるようにします。

クレジットの箇所を読むと、LTDの元になったScott McKay氏の書いたlisp-to-dylan.lispはエディタのバッファを置き換えるというものだったらしいので、なんとなく逆行してしまっている気しますが、表示されたDylanのコードを眺めるのも面白いので、これはこれで良いかなと思います。

変換されたコードでは、きちんとコメントが反映されていたり、マクロは展開されたりします。

動かすのに必要なもの

  1. slime.el
  2. dylan-mode.el

他、ANSI CLで廃止になった記述等がありコンパイルできない箇所がある(SBCL)ので適当に直します。

  1. LTDはCL-USERパッケージを前提にしているので、ltdというパッケージを利用するように変更。
  2. read.lispのrecord-file-positionsの置かれている位置を利用している関数より先に持ってゆく
  3. loopの中で、by 'cddrというのを、by #'cddrに直す
  4. string-char型は廃止なので、characterに変更

SLIME側のコード
;; LtD 
(eval-after-load "slime"
  '(progn
     (defun slime-ltd (times)
       (interactive "p")
       (slime-eval-and-ltd
        `(swank:eval-and-grab-output
          ,(format "(WITH-INPUT-FROM-STRING (IN (PRIN1-TO-STRING '%s))(LTD::LTD-EXP IN *STANDARD-OUTPUT*))" 
                   (slime-defun-at-point)))))
     
     (defun slime-eval-and-ltd (form)
       (slime-eval-async form (slime-rcurry #'slime-show-ltd
                                            (slime-current-package))))
     
     (defun slime-show-ltd (string package)
       (slime-with-popup-buffer ("*SLIME LtD*" package t t)
         (dylan-mode)
         (princ "// LtD")
         (terpri)
         (terpri)
         (princ (first string))
         (goto-char (point-min))))
     
     ;; SUPER-SHIFT-D
     (define-key slime-mode-map
       [(super shift ?d)] 'slime-ltd)))

2010-05-11

KMRCLを眺める(151) ESCAPE-BACKSLASHES

| 14:13 | KMRCLを眺める(151) ESCAPE-BACKSLASHES - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(151) ESCAPE-BACKSLASHES - わだばLisperになる

今回もKMRCLのstring.lispの残り、ESCAPE-BACKSLASHESです。

strings.lispも今回で最後。

動作は、

(KL:ADD-SQL-QUOTES "foo'")
⇒ "foo''"

という風に、クォートを二重にして返します。

定義は、

(defun add-sql-quotes (s)
  (substitute-string-for-char s #\' "''"))

となっていて、内部では、 SUBSTITUTE-CHARS-STRINGS を利用しています。

2010-05-10

(13)Cambridge Lisp(1985)

| 20:29 | (13)Cambridge Lisp(1985) - わだばLisperになる を含むブックマーク はてなブックマーク - (13)Cambridge Lisp(1985) - わだばLisperになる

CiNiiのLISP関係の論文をのんびり漁っております。今回は、

です。

Cambridge Lispとは、イギリスのケンブリッジ大学で開発されていたLISP処理系だそうで、Standard Lispの系統とのこと。

処理系の歴史/実装が紹介されています。

当時、Common Lispが台頭し始めている頃ですが、この頃の論文を読んでいると、Common Lispも良いけど、世の中が統一されるのもなんだかつまらないなあというような意見が多いのが面白いです。

Common Lisp登場の歴史的な説明として、「様々な方言が乱立して困っていた」というのがありますが、どちらかというと「LISPなので移植は簡単」という意見や記述の方が当時は多かったようにも思えます。

2010-05-09

KMRCLを眺める(150) ESCAPE-BACKSLASHES

| 19:20 | KMRCLを眺める(150) ESCAPE-BACKSLASHES - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(150) ESCAPE-BACKSLASHES - わだばLisperになる

今回もKMRCLのstring.lispの残り、ESCAPE-BACKSLASHESです。

動作は、

(KL:ESCAPE-BACKSLASHES "foo\\bar\\baz")
⇒ "foo\\\\bar\\\\baz"

というところで、名前のとおりバックスラッシュをエスケープ処理した文字列を返します

定義は、

(defun escape-backslashes (s)
  (substitute-string-for-char s #\\ "\\\\"))

となっていて、内部では、 SUBSTITUTE-CHARS-STRINGS を利用してバックスラッシュを追加しています。

自分もすっかり忘れていたのですが、CLでは、文字列中のバックスラッシュ(とダブルクォート)はエスケープして表現する必要があります。

(LENGTH "123\456\7890")
;⇒ 10

(LENGTH "123\\456\\7890")
;⇒ 12

ちなみに、シンボルの場合も似ていますが、文字列とシンボルでは、ダブルクォートとパイプの役割が逆転した感じになっています。

'|"|
;⇒ |"|

"|"
;⇒ "|"

'|\||
;⇒ |\||

"\""
;⇒ "\""

"\\"
;⇒ "\\"

'|\\|
;⇒ |\\|

'|"\foo"|
;⇒ |"foo"|

"|\foo|"
;⇒ "|foo|"

2010-05-08

StumpWMの日々 (4)

| 15:53 | StumpWMの日々 (4) - わだばLisperになる を含むブックマーク はてなブックマーク - StumpWMの日々 (4) - わだばLisperになる

StumpWMではGNU Screenや、Emacsのようにキー操作で画面を切り替えたりします。

EmacsのC-xのようにプレフィックスキーと他のキーを組み合わせる操作が殆どなのですが、これだと2ストロークになってしまいボリュームの操作などには面倒です。

ちょっと調べたら、*top-map*というキーマップに登録すれば対象のキー一つでコマンドが実行できる様子。

ということで設定

(PROGN
  ;; amixer
  (LOAD "stumpwm/contrib/amixer")
  (DEFINE-KEY *TOP-MAP* (KBD "XF86AudioRaiseVolume") "amixer-master-1+")
  (DEFINE-KEY *TOP-MAP* (KBD "XF86AudioLowerVolume") "amixer-master-1-")
  (DEFINE-KEY *TOP-MAP* (KBD "XF86AudioMute") "amixer-master-toggle")
  )

こんな感じでボリューム操作ができるようになりました。

2010-05-07

StumpWMの日々 (3)

| 20:27 | StumpWMの日々 (3) - わだばLisperになる を含むブックマーク はてなブックマーク - StumpWMの日々 (3) - わだばLisperになる

KMRCLとCiNiiのことばかりなので、久々にStumpWMのことでも書いてみようかなと思います

StumpWMを使い出してからなんだかんだで3年位になろうとしていますが、いまいちどうやったら良いのか良く分からないことが結構あります。

例えば、

  1. ウィンドウのグループ/配置を記憶しておいて、ウィンドウマネージャー起動時にお気に入りの配置に復帰させる

方法が分かりません。

ものぐさなのでこの3年間ずっと放置していましたが、ちょっと挑戦してみることにしました。

ウィンドウの配置は、 dump-desktop でファイルに書き出すことができます。

デフォルトでは、どうやら、~/.stumpwm.dディレクトリに書き出される様子。

初期化ファイルで、restore-from-fileを使い、ダンプしたものを読み込めばグループの構成自体は復帰できるようです。

また、GSELECTという関数でグループを変更できるので、グループを変更→アプリを起動→次のグループへ を繰り返せば、目的は達成できるのではないかと考え、

(RESTORE-FROM-FILE "~/.stumpwm.d/g000001")

(PROGN
  ;; emacs用のグループ選択
  (GSELECT (FIND-GROUP (CURRENT-SCREEN) "emacs"))
  (EMACS)
  (GSELECT (FIND-GROUP (CURRENT-SCREEN) "skype"))
  (SKYPE)
  (GSELECT (FIND-GROUP (CURRENT-SCREEN) "web"))
  (CHROME)
  (FIREFOX)
  (GSELECT (FIND-GROUP (CURRENT-SCREEN) "shell"))
  (SCREEN)
  )

のように書いてみました。

結果から書くとこの書き方では駄目で、アプリの起動とグループの切り替えはバラバラに動いてしまい最後に選択したグループに全部のアプリが寄ってきてしまいます。

うーん、みなさんどうやってるんでしょう。

2010-05-05

KMRCLを眺める(149) SUBSTITUTE-STRING-FOR-CHAR

| 15:18 | KMRCLを眺める(149) SUBSTITUTE-STRING-FOR-CHAR - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(149) SUBSTITUTE-STRING-FOR-CHAR - わだばLisperになる

今回もKMRCLのstring.lispの残り、SUBSTITUTE-STRING-FOR-CHARです。

動作は、

(SUBSTITUTE-STRING-FOR-CHAR "foo bar baz" #\Space "    ")
⇒ "foo    bar    baz"

というところで、指定した文字を指定した文字列に置換します。

定義は、

(defun substitute-string-for-char (procstr match-char subst-str)
  "Substitutes a string for a single matching character of a string"
  (substitute-chars-strings procstr (list (cons match-char subst-str))))

となっていて、内部では、 SUBSTITUTE-CHARS-STRINGS を利用しています。

2010-05-04

(12)Lisp システムにおけるデバッギング・ツール(1979)

| 16:43 | (12)Lisp システムにおけるデバッギング・ツール(1979) - わだばLisperになる を含むブックマーク はてなブックマーク - (12)Lisp システムにおけるデバッギング・ツール(1979) - わだばLisperになる

CiNiiのLISP関係の論文をのんびり漁っております。今回は、

です。

まず最初にデバッグとはどういうことかを考察し、その後、具体例として、LISPシステムでのデバッギング・ツールを紹介しています。

ツールとしては、

LISP 1.5では、

  1. トレーサー (CLでいうTRACE)

InterLispでは、

  1. DWIM(自動スペル修正機能)
  2. ブレイク・パッケージ(エラーでの対話的な処理/CLでいうBREAKなど)
  3. ADVICE機能(CLでいうとメソッドコンビネーション、Emacs Lispのadvice機能の方が近い)
    • 目的の関数にフックを掛けたりラップしたりする
  4. LISP専用エディタ
  5. プリティ・プリンタ
  6. バック・トレーサ
  7. 構造解析ルーチン(PRETTYSTRUCTURE)
    • 関数間の呼び出し関係を出力したり、変数の束縛、自由変数等々を調べることができるものがあったようです。
  8. UNDO機能
    • 対になる実行した内容を打ち消すようなコマンドを保存しておいてUNDO時に実行するという仕掛けだったらしい

Lisp 1.9では、

  1. 入出力デバッグ機能
  2. 機械語レベルでのデバッグシステムとの連携

Lisp Machine Lispでは

  1. エラー処理用関数のユーザーへの開放
  2. MAR機能(機械語レベルでのデバッグシステムとの連携をLISPで記述/操作できる)

等々ですが、現在も30年前もそれ程変っていないように思えます。

Slime等にも上記のようなデバッグに使える便利な機能はあるようですが、自分は、あまり積極的に使ったことが無かったので、ちょっと見直してみたいと思いました。

2010-05-03

KMRCLを眺める(148) TRIM-NON-ALPHANUMERIC

| 15:53 | KMRCLを眺める(148) TRIM-NON-ALPHANUMERIC - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(148) TRIM-NON-ALPHANUMERIC - わだばLisperになる

前回で一通りstring.lispは眺め終わったと思ったのですが、何点か抜かしていたようなので、順番が前後しつつ、今回はKMRCLのTRIM-NON-ALPHANUMERICです。

動作は、

;; SBCL
(MAPCAR #'KL:TRIM-NON-ALPHANUMERIC
        '("いろはにほへど、"
          "   ちりぬるを、"
          " わかよたれそ、"
          "        つねならむ"))
;⇒ ("いろはにほへど" "ちりぬるを" "わかよたれそ" "つねならむ")

というところで、STRING-TRIMとNON-ALPHANUMERICPが合体したようなもの。

定義は、

(defun trim-non-alphanumeric (word)
  "Strip non-alphanumeric characters from beginning and end of a word."
  (declare (simple-string word)
           (optimize (speed 3) (safety 0) (space 0)))
  (let* ((start 0)
         (len (length word))
         (end len))
    (declare (fixnum start end len))
    (do ((done nil))
        ((or done (= start end)))
      (if (alphanumericp (schar word start))
          (setq done t)
        (incf start)))
    (when (> end start)
      (do ((done nil))
          ((or done (= start end)))
        (if (alphanumericp (schar word (1- end)))
            (setq done t)
          (decf end))))
    (if (or (plusp start) (/= len end))
        (subseq word start end)
      word))

となっていて、左端を算出して、次に右を算出して、SUBSEQで切り出しという感じです。

TRIM-NON-ALPHANUMERICを眺めていて、STRING-TRIM-IFのようなものを誰か考えていそうだなあ、と思ったので調べてみると、METATILITIESに定義がありました。

(MAPCAR (CURRY #'metatilities:STRING-TRIM-IF
               (COMPLEMENT #'ALPHANUMERICP))
        '("いろはにほへど、"
          "   ちりぬるを、"
          " わかよたれそ、"
          "        つねならむ"))
;⇒ ("いろはにほへど" "ちりぬるを" "わかよたれそ" "つねならむ")

個人的にはこういう方がすっきりしてて好みです。

2010-05-02

(11)Common Lisp検証システム(1993)

| 18:11 | (11)Common Lisp検証システム(1993) - わだばLisperになる を含むブックマーク はてなブックマーク - (11)Common Lisp検証システム(1993) - わだばLisperになる

CiNiiのLISP関係の論文をのんびり漁っております。今回は、

です。

CLtL2のように大きい仕様の処理系を開発する際に必要になってくる処理系の機能検証システムについて、実際のTachyon CLの開発例を元に述べられています。

システムの構成、使い勝手、遭遇した問題等々、色々参考にできるところが多いと思いました。

2010-05-01

KMRCLを眺める(147) STRING->LIST

| 15:57 | KMRCLを眺める(147) STRING->LIST - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(147) STRING->LIST - わだばLisperになる

今回はKMRCLのstrings.lispから、STRING->LISTです。

名前からすると、(coerce 文字列 'list)の別名かと思えますが、動作は、

(KL:STRING->LIST "いろはにほへと")
⇒ (いろはにほへと)

(KL:STRING->LIST "foo bar baz")
⇒ (FOO BAR BAZ)

という感じです。

実装を眺めてみると、

(defun string->list (string)
  (let ((eof (list nil)))
    (with-input-from-string (stream string)
      (do ((x (read stream nil eof) (read stream nil eof))
           (l nil (cons x l)))
          ((eq x eof) (nreverse l))))))

文字列をストリームとして読み込んでREADしてリストにして返すということみたいです。

ということで、

(KL:STRING->LIST "(foo bar baz)")
⇒ ((FOO BAR BAZ))

(KL:STRING->LIST "foo: bar: baz:")
>>> 読み込みエラー

ということにもなります。