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 |

2011-05-29

'T と () は劣悪なパッケージ環境でも平気

| 17:09 | 'T と () は劣悪なパッケージ環境でも平気 - わだばLisperになる を含むブックマーク はてなブックマーク - 'T と () は劣悪なパッケージ環境でも平気 - わだばLisperになる

以前にも書いたMacLISPのコードに散見される誰かのスタイルですが、どうでも良いメリットを発見したのでメモしておきます。

そのメリットとは、'Tも () リードマクロが関係するために劣悪なパッケージ環境にも強いということです!

(make-package :foo :use nil)

(in-package :foo)

(cl:when '()
  "foo!")
;=> COMMON-LISP:NIL

(cl:when 'nil
  "foo!")
;=> "foo!"

(cl:when t
  "foo!")
;>>> error

(cl:when 't
  "foo!")
;=> "foo!"

(cl:when (quote ())
  "foo!")
;>>> error

(cl:when (quote nil)
  "foo!")
;>>> error

(cl:when (quote t)
  "foo!")
;>>> error

2009-10-08

謎のこだわり 'Tと()

| 00:22 | 謎のこだわり 'Tと() - わだばLisperになる を含むブックマーク はてなブックマーク - 謎のこだわり 'Tと() - わだばLisperになる

全く誰も興味がない話題だと思うのですが、MacLISPのソースコードを眺めているとどういう訳か、NILという表記が少なく、()と書かれている割合が多いようです。

そして、Tは、クォートされて'Tと書かれていることが多い気がします。

どんな感じかというと

(DEFUN CHMP1 (X)                                ;"CHOMP" one function
       (SETQ DATA (GETL X '(EXPR FEXPR)) CFVFL () LAPLL () )
       (COMPILE X (CAR DATA) (CADR DATA) () () )
       (LAP-A-LIST (SETQ LAPLL (NREVERSE LAPLL)))
       (AND (COND ((SYSP X) 
                   (AND (SETQ DATA (GETL X '(EXPR FEXPR SUBR FSUBR LSUBR)))
                        (MEMQ (CAR DATA) '(EXPR FEXPR))
                        (SETQ DATA '(SUBR FSUBR LSUBR))))
                  ('T (AND (SETQ DATA (GETL X '(*EXPR *FEXPR *LEXPR SUBR FSUBR LSUBR)))
                           (MEMQ (CAR DATA) '(SUBR FSUBR LSUBR))
                           (SETQ DATA '(*EXPR *FEXPR *LEXPR)))))
            (SETQ DATA (CAR (GETL X DATA)))
            (PUTPROP X (CAR (REMPROP X DATA)) DATA)))

のような感じなのですが、condのelse節に'Tが使われていて、nilと書きそうなところも()です。

どうもMacLISPのメインメンテナだったJonL White氏が触ったところは、こういう風にNIL ⇒ ()、T ⇒ 'Tのスタイルで書かれているんじゃないかと思われ、全体として()の割合が多くなっている気がするのですが、一体どういう理由でこうなのかが謎です。

ちょろっとある更新記録にも

;;; 01/29/81 jonl - flushed (STATUS FEATURE MACAID) and NIL --> () 
;;;                 added special declaration for GRIND-MACROEXPANDED
;;; 05/25/78 jonl - Had GRINDEF and SPRINTER lambda-bind variable LINEL, and 
;;;                 removed all references to CONNIVER and PLANNER stuff.
;;;                 Flush "SGPLOSES" and NOARGS calls; flush GBREAK.
;;;                 Change "NIL" into "()".

などと書かれています。そんなに()が好きなのか。もしくは、NILが嫌いなのか。GRINDEFのファイルだけにプリティプリンタの挙動か。それとも単なる偶然か。

MacLISPのソースは、

にありますので、興味のある方は覗いてみて下さい。

また、真相をご存知の方は是非教えて下さい!

ちなみに、Tにクォートを付けるのは真似してやってみたら割と癖になってしまい、自分がLISPを書き始めた頃から大体これで書くようになってしまいました。CONDのelse節に使うと目立つのが気に入ってますが、他に良いことは別にないです。あえて言うなら、クォートを付けると私の中ではブール値っぽさが増します。NILも全部()で書いてみたら何か発見があるのかもしれない…。

2008-04-07

MacLISPでは表示と入力の基数にローマ数字が設定できた

| 19:49 | MacLISPでは表示と入力の基数にローマ数字が設定できた - わだばLisperになる を含むブックマーク はてなブックマーク - MacLISPでは表示と入力の基数にローマ数字が設定できた - わだばLisperになる

前回のPAIP勉強会の後のちょっとした飲み会でも話のネタにしたのですが、MacLISPは、システムの基数にローマ数字を指定できました。

非常に馬鹿っぽくて大好きなので、これをネタに、またpartty.orgでキャプチャーしてみました。

どういうことかというと、MacLISPでは、表示用の基数をbase、入力用の基数をibaseで指定可能で標準は、8(8進数)です。

ここに、romanという指定をすると、iや、mcm等が、1や、1900として扱われることになります。

ちなみに、入力と表示は分かれているので、入力と表示で一致しないこともあります。

この機能は単にハックバリュー(つまり、ただ作ってみたかったから作っただけ)の為に追加されたとのことで、ジャーゴンファイルのhack valueの項目にも説明があります。

基数にローマ数字を設定した場合、iや、xが数字になるので、変数の扱いには注意が必要です。また、MacLISPのマニュアルによると、コンパイルの時の挙動も予測できないものになりがちなので基数にローマ数字を設定するな、というような注意書きがあったりします。

ちなみに、formatのローマ数字のサポート機能も、この基数をローマ数字にするのもGuy Steel氏の仕業だったと記憶しているのですが、改めて資料を探しても見付かりませんでした…。夏休みに実装した、ってのをどっかで読んだ筈なんですが、詳細を知ってる方がいたら教えて下さい…。

なんにしろ、注意書きを書くことはあっても、こんな機能を削除しないというMacLISPは素晴しいですね。

2008-03-20

30年前のオリジナルEMACSとLisp開発環境

| 00:49 | 30年前のオリジナルEMACSとLisp開発環境 - わだばLisperになる を含むブックマーク はてなブックマーク - 30年前のオリジナルEMACSとLisp開発環境 - わだばLisperになる

最近サービスが開始されたPartty!.orgが面白そうだったので、オリジナルのEMACSを操作しているところをアップしてみたら受けるかもしれないということでキャプチャしてアップしてみました。

EMACSは32年位前にITS(MITで稼働していたMITのOS)で生まれ、それから同じくDECのPDP-10で稼働するTOPS-20等に移植されたのですが、このキャプチャはTOPS-20のものです。本当は、ITSの操作を取り込みたかったのですが、画面の描画が崩れてしまうので、しょうがなくTOPS-20で…。といっても見た目的にはそんなに違いはありません。

キャプチャについて大まかに解説すると、まず、MacLISPを起動しています。自分はMacLISPの初期化ファイルで、LEDITというEMACSとMacLISPが連携するLISP開発環境を呼び出しているので、起動後にすぐEMACSも起動します。

その後、適当にfib関数を定義したりしてみています。LEDITは、選択した範囲をMacLISPに送信する機能があるので、範囲を選択して送信すると、MacLISPでは送信された式が評価されます。そして、ショートカットでぱぱっとEMACSからMacLISPに抜けたり、戻ったりできます。

次に、carとcdrを拡張して定義できるcarcdr.lspというものをロードしてみています。

def-carcdrというマクロがあるので、適当にcadd...rのようなものを定義して遊んでみています。

その後、INFOを呼び出してLEDITの説明を表示してみています。

INFOもITSが生まれ故郷で、ITSでは、UNIXのmanのようにINFOが機能していました。

ほんのさわりしか紹介していないのですが、30年前のEMACSの時点でLISP式の編集に関しては、ほぼ完成してしまっています。

そして、70年代から、LISPのコーディングはこういう編集支援と共に育って来ていて、30年前の時点でLISPのメッカでは支援なしの素のLISPであれこれするということは既に行われていなかった様子です。

2008-01-31

末尾再帰的DEFUN (2)

| 04:53 | 末尾再帰的DEFUN (2) - わだばLisperになる を含むブックマーク はてなブックマーク - 末尾再帰的DEFUN (2) - わだばLisperになる

何となく釈然としないまま、一旦放置した末尾再帰的DEFUNですが、何となく眺めていると末尾再帰をgo-toに変換するんじゃないのかなあ、という気がしてきました。

つまり明示的に末尾再帰で書かれたものを、完全なループに変換するという目的のものだったのではないかと思えてきました。

そう考えると、関数呼び出しの個所をgo-toに変換すれば良いのですが、

(defun fib (n &optional (a1 1) (a2 0))
  (if (< n 2)
      a1
      (fib (1- n) (+ a1 a2) a1))))

のような末尾再帰の定義は

(defun fib (n &optional (a1 1) (a2 0))
  (prog ()
    L   (if (< n 2)
	    (return a1)
	    ((lambda (t1 t2 t3) (setq n t1 a1 t2 a2 t3) (go L)) (1- n) (+ a1 a2) a1)))))

のようにすれば、マクロで置き換えるのも、そんなに大変でもないかなと。

本当は、

(defun fib (n &optional (a1 1) (a2 0))
  (prog ()
    L   (if (< n 2)
	    (return a1)
	    (progn (setq n (1- n) a1 (+ a1 a2) a2 a1) (go L))))))

という風にするべきな気もします。スタックの使われ方とか、その辺に違いがありそうですが、disassemしても良く分からなかったので、とりあえず、lambdaの方で行くことにしました。

それで、この場合、PROGの中に展開されるので、最終的に値を返すところには、returnを付けないといけない訳なのですが、それがどこなのか判別するのは至難の技なので、逆にRETURNの中に展開してしまうことにしました。オリジナルもこういう感じなのですが、こういうことなのかも知れません。

(defun fib (n &optional (a1 1) (a2 0))
  (prog ()
    L   (return (if (< n 2)
		    a1
		    ((lambda (t1 t2 t3) (setq n t1 a1 t2 a2 t3) (go L)) (1- n) (+ a1 a2) a1))))))

そんなこんなでいつものごとくガチャガチャと自分なりに作ってみました。

RETURN式の中から外にgotoとかして良いのかしら、とか思ったりしますが、これって手法としてはありななんですかねえ。

;; 動作
(tail-recursive-defun fib (n &optional (a1 1) (a2 0)) 
  (if (< n 2)
      a1
      (fib (1- n) (+ a1 a2) a1)))

;; マクロ展開=>
(DEFUN FIB (N &OPTIONAL (A1 1) (A2 0))
  (PROG ()
     #:G3105
     (RETURN
       (IF (< N 2) 
	   A1
	   ((LAMBDA (#:G3106 #:G3107 #:G3108)
	      (SETQ N #:G3106 A1 #:G3107 A2 #:G3108)
	      (GO #:G3105))
	    (1- N) (+ A1 A2) A1)))))

;; 定義 --------
;; 関数呼び出し部分をgo-to付きのlambda式で置き換え
(defun fn-to-lambda (new old expr)
  (flet ((self (x) (fn-to-lambda new old x)))
    (cond ((atom expr) expr)
	  ((and (consp expr) (eq (car expr) old))
	   (cons new (mapcar #'self (cdr expr))))
	  ('T (cons (funcall #'self (car expr)) (mapcar #'self (cdr expr)))))))

;; 関数をgo-to付きのlambda式に変換
(defun funcall-to-goto (args gotag)
  (let ((syms (mapcar (lambda (x) `(,x ,(gensym))) args)))
    `(lambda ,(mapcar #'cadr syms) (setq ,@(mapcan #'identity syms)) (go ,gotag))))

;; 余計なパラメータを削除
(defun remove-&param (expr)
  (mapcar (lambda (x) (if (consp x) (car x) x))
	  (remove-if (lambda (x) (member x '(&optional &rest &key))) expr)))

;; 本体
(defmacro tail-recursive-defun (name args &body body)
  (let ((go-tag (gensym))
	(decl (if (eq 'declare (and (consp (car body)) (caar body)))
		  `(,(pop body))
		  ())))
    `(defun ,name ,args
       ,@decl
       (prog ()
	  ,go-tag
	  (return
	    ,@(fn-to-lambda (funcall-to-goto (remove-&param args) go-tag) name 
			    body))))))
 ;

末尾再帰的DEFUN

| 02:12 | 末尾再帰的DEFUN - わだばLisperになる を含むブックマーク はてなブックマーク - 末尾再帰的DEFUN - わだばLisperになる

今日は、Arcもいじっていたのですが、なんとなくSAILのMACLISPのコードも漁っていました。

SAILのものは非常に野心的というか、変態的というか、妙なコードが多いのですが、ふと以前から気になっていたTAIL-RECURSIVE-DEFUNのコードを追っ掛けてみることにしました。

実際のコードはこちらです。

SAILの変態っぷりは、恐らくRichard P.Gabriel氏によるところが非常に大きいと思うのですが、何となくこのTAIL-RECURSIVE-DEFUNもそんな香りがします。

とりあえず、探ってみたいのは、このコードです。

(DEFUN (TAIL-RECURSIVE-DEFUN MACRO)(X)
  ((LAMBDA(?F-NAME *TYPE)
    ((LAMBDA(*ARGS *DEFINITION)
      ((LAMBDA(?GO-LABEL)
	(α-GRAB-TAILS *ARGS *DEFINITION ?GO-LABEL)
	(CCODE (DEFUN ?F-NAME *TYPE (*ARGS) (PROG NIL
						  ?GO-LABEL
						  (RETURN (PROGN *DEFINITION))))))
       (GENSYM)))
     (COND (*TYPE (CADDDR X))(T (CADDR X)))
     (COND (*TYPE (CDDDDR X))(T (CDDDR X)))))
   (CADR X)
   (COND ((MEMQ (CADDR X) '(EXPR FEXPR))
	  (LIST (CADDR X)))
	 (T NIL))))
 ;

これは一体何をするものなのか。自動で末尾再帰に変換してくれるのか。それとも他に末尾再帰的な何かの特長があるのか、謎です…。

とりあえず、もの凄くLAMBDAがネストしているのですが、これはLETの役割です。それで、DEFUNになっているのですが、MACLISPでは、DEFUNでマクロも定義でき、この場合、マクロを定義しています。

最終的には、(defun foo (n) ...body)のように展開されたものができるんじゃないかと思います。

それでこのTAIL-RECURSIVE-DEFUNが依存している関数で独自に定義されたものを追っ掛けてみます。

とりあえず、MACLISPからCLへ移植してみました。CLにないMACLISP標準は自作しています。

(DEFUN ANY-MEMQ(X Y)
  (COND ((NULL Y)NIL)
	((ATOM Y)(EQ X Y))
	(T(OR (ANY-MEMQ X (CAR Y))
	      (ANY-MEMQ X (CDR Y))))))

;(any-memq 'x '(y (((((x(((())))))))) z))
;=> t

(defmacro ccode (X) `(DO-CODE ,x))

(DEFUN DO-CODE(X)
  (COND ((NULL X)NIL)
	((ATOM X)
	 ((LAMBDA(CHAR1)
	   (COND ((MEMQ CHAR1 '(? *))X)
		 (T (LIST 'QUOTE X))))
	  (GETCHAR X 1)))
	((AND (ATOM (CAR X))(EQ '* (GETCHAR (CAR X) 1)))
	 (LIST 'APPEND (DO-CODE (CAR X)) (DO-CODE (CDR X))))
	(T(LIST 'CONS (DO-CODE (CAR X)) (DO-CODE (CDR X))))))

(DEFUN α-GRAB-TAILS (ARGS DEF ?GO-LABEL)
 (COND ((ATOM DEF)NIL)
       ((AND (ATOM(CAR DEF)) (EQ 'TAIL-RECUR (CAR DEF)))
	(COND ((EQUAL ARGS (CDR DEF))		;calling with same args!
	       (RPLACA DEF 'GO)
	       (RPLACD DEF (LIST ?GO-LABEL)))
	      (T(DO ((ARGS ARGS (CDR ARGS))
		     (NEWARGS (CDR DEF) (CDR NEWARGS))
		     (SETS NIL (NCONC SETS
				      (COND ((EQ (CAR ARGS) (CAR NEWARGS))
					     NIL)
					    (T (NCONS
						((LAMBDA(SYM)
						  (CONS (CONS (CAR ARGS)SYM)
							(LIST 'SETQ
							      (CAR ARGS)
							      (SUBLIS (MAPCAR 'CAR
									      SETS)
								      (CAR NEWARGS)))))
						 (GENSYM))))))))
		    ((NULL ARGS)
		     ((LAMBDA(L-EXP)
		       (RPLACA DEF (CAR L-EXP))
		       (RPLACD DEF (CDR L-EXP)))
		      (α-OPTIMIZE-λ (MAPCAR 'CDAR SETS)
				    (NCONC (MAPCAR 'CDR SETS)
					   (NCONS(LIST 'GO ?GO-LABEL)))
				    (MAPCAR 'CAAR SETS))))))))
       (T(MAPC (FUNCTION(LAMBDA(DEF)
			 (α-GRAB-TAILS ARGS DEF ?GO-LABEL)))
	       DEF))))

(DEFUN α-OPTIMIZE-λ (VARS BODY BINDINGS)
  (DO ((VARS VARS (CDR VARS))
       (BINDINGS BINDINGS (CDR BINDINGS))
       (NVARS NIL (NCONC NVARS
			 (COND ((ANY-MEMQ (CAR VARS) BODY)(NCONS (CAR VARS)))
			       (T NIL))))
       (NBINS NIL (NCONC NBINS
			 (COND ((ANY-MEMQ (CAR VARS) BODY)(NCONS (CAR BINDINGS)))
			       (T NIL)))))
      ((NULL VARS)(CONS (CONS 'LAMBDA (CONS NVARS BODY))
			NBINS))))

;; オリジナルに割と忠実版
(defmacro TAIL-RECURSIVE-DEFUN (&whole X &body body)
  (declare (ignore body))
  ((LAMBDA(?F-NAME *TYPE)
     ((LAMBDA(*ARGS *DEFINITION)
	((LAMBDA(?GO-LABEL)
	   `(progn
	      ,@(α-GRAB-TAILS *ARGS *DEFINITION ?GO-LABEL)
	      (DEFUN ,?F-NAME ,*TYPE (,@*ARGS) (PROG NIL
						  ,?GO-LABEL
						  (RETURN (PROGN ,@*DEFINITION))))))
	 (GENSYM)))
      (COND (*TYPE (CADDDR X))(T (CADDR X)))
      (COND (*TYPE (CDDDDR X))(T (CDDDR X)))))
   (CADR X)
   (COND ((MEMQ (CADDR X) '(EXPR FEXPR))
	  (LIST (CADDR X)))
	 (T NIL))))

;; バッサリとMACLISP特有の部分を切り捨てた版
(defmacro TAIL-RECURSIVE-DEFUN (?f-name *args &body *definition)
  (let ((?GO-LABEL (gensym)))
    `(progn
       ,(α-GRAB-TAILS *ARGS *DEFINITION ?GO-LABEL)
       (DEFUN ,?F-NAME (,@*ARGS) 
	 (PROG NIL
	    ,?GO-LABEL
	       (RETURN (PROGN ,@*DEFINITION)))))))


;; ML標準の関数達
(defun ncons (n) (list n))

(defun memq (x y)
  (member x y :test #'eq))

(defun getchar (x index)
  (values (intern (string (char (string x) (1- index))))))
 ; 

中身の動作なのですが、とりあえず、

(α-GRAB-TAILS '(x y z) '(tail-recur) 'go)
;-> ((LAMBDA () (SETQ X NIL) (SETQ Y NIL) (SETQ Z NIL) (GO GO))) 

(α-GRAB-TAILS '(x y z) '(tail-recur x) 'go)
;-> ((LAMBDA () (SETQ Y NIL) (SETQ Z NIL) (GO GO))) 

(α-GRAB-TAILS '(x y z) '(tail-recur y z x) 'go)
;-> ((LAMBDA (#:G2999) (SETQ X Y) (SETQ Y Z) (SETQ Z #:G2999) (GO GO)) X) 

(α-GRAB-TAILS '(x y z) '( y z x) 'go)
;-> (Y Z X) 

(do-code '(?foobarbaz hello one))
;(CONS ?FOOBARBAZ (CONS 'HELLO (CONS 'ONE NIL))) 

(do-code '(*foobarbaz hello one))
;(APPEND *FOOBARBAZ (CONS 'HELLO (CONS 'ONE NIL))) 

(tail-recursive-defun xyz (x y z)
  tail-recur
  (list x y z))
; マクロ展開結果
;->
(PROGN
 ((LAMBDA () (SETQ X (LIST X Y Z)) (SETQ Y NIL) (SETQ Z NIL) (GO #:G2995)))
 (DEFUN XYZ (X Y Z)
   (PROG ()
    #:G2995
     (RETURN
      (PROGN
       ((LAMBDA ()
          (SETQ X (LIST X Y Z))
          (SETQ Y NIL)
          (SETQ Z NIL)
          (GO #:G2995))))))))

という感じです。

α-OPTIMIZE-λは、どうやら不要な変数束縛を取り除いて簡略化するもののようで、これは理解できました。

これを呼び出しているα-GRAB-TAILSが良く分からないのですが、名前からすると、末尾部分を抽出するもののようなのですが、動きが良く分からない…。

何がどう末尾再帰なのか…、纏められないままエントリを終わります(笑)

2007-09-16

.3

| 00:31 | .3 - わだばLisperになる を含むブックマーク はてなブックマーク - .3 - わだばLisperになる

今回は、以前から分析してみたかったコードに挑戦してみました。

お題は、30年以上前のコードと思われるMaclispのrdsylの定義です。

Maclisp/maklap.lsp

http://www.google.com/codesearch?hl=ja&q=+DEFUN%5C+RDSYL+show:vVmLEzSQCwQ:uCPQVGUIK4w:K_YYdzk_7EY&sa=N&cd=1&ct=rc&cs_p=ftp://ftp.ultimate.com/pdp10/maclisp/maclsp804.tar.gz&cs_f=maclsp804/maklap.lsp#a0

(DEFUN RDSYL (L DF) 
  (PROG (LL BRAKP ANS CH)
	(SETQ DF (MERGEF DF '((* *) * *)))
     AA	(SETQ LL (SETQ BRAKP () ))
     A	(SETQ CH (OR (CAR L) #/_))
        (COND ((OR (= CH #/^Q) (= CH #//))	 			;"/", "^Q" ;^Qはコントロール文字
	       (POP L)
	       (SETQ CH (CAR L)))
	      ((AND (= CH #/[) (NOT #%(ITSP)))				;"["
	       (SETQ BRAKP 'T))
	      ((AND (= CH #/]) (NOT #%(ITSP))) (SETQ BRAKP () ))	;"]"
	      ((OR (= CH #/( ) (= CH #/) )) (RETURN () ))		;Cant have parens here
	      ((= CH #/,)					;Comma
	       (COND ((NOT BRAKP)
		      (POP L)
		      (GO RET))))
	      ((= CH #/_) (GO RET)))
	(PUSH CH LL)
	(POP L)
	(GO A)
   RET  (SETQ DF (MERGEF (NAMELIST (MAKNAM (NREVERSE LL))) DF))
	(SETQ ANS (NCONC ANS (LIST DF)))
	(AND (= CH #/,) (GO AA))
	(RETURN ANS) ))

だいぶ前に、自分もスティーブン・レビーの「ハッカーズ」を図書館から借りて読みました。それで、Greenblatt氏に興味を持って適当に検索していたら、lispmeister.comのブログでcomp.lang.lispで、奇怪なコードが典型的なGreenblatt氏のスタイルとして紹介されている、というエントリを目にして以来いつか分析してみたいなと思っていました。

http://lispmeister.com/blog/lisp-news/richard-greenblatt.html

日本のLispUser.netさんのところでも紹介されてます。

http://lispuser.net/memo/lisp/2006-06-25-10-20.html

ということで、写経してみたいのですが、ちょっとこれは動作自体が想像できず、記憶もできないので、今回は、分解して一体何をするものなのかということを解明してみることにします。

前準備:

  • 全体的な動作

コードから動作が想像できないので、実際にMaclispを実際に動かしてみて確認してみたところ、どうやらファイル名を受け取ってシステムで使用できる名前に整える関数のようです。

  • エスケープ文字

Common Lispだと、キャラクタは、#\Aの様に表現されますが、Maclispは、/がエスケープ文字なので、#/Aの様に表現されます。

  • 関数名:RDSYL

Read Symbol Listとかその辺の短縮じゃないかと思うのですが実際のところ何なんでしょう。

  • MERGEF

Common Lispのmerge-pathnamesみたいな関数です。

  • NAMELIST

デバイスとディレクトリ、ファイル名、拡張子をシステムが扱える形式にして出力する関数のようです。

  • MAKNAM

何とも説明しにくいのですが、

(defun maknam (syms)
  (intern
   (map 'string (lambda (x) (char (string x) 0))
	syms)))

のような動作をする関数です。

  • #%

#.とか#,とかその辺の動作と同じなんでしょうか、詳細は分かりませんでした。あってもなくても大体の動作は同じでした。

  • ITSP

ITS上のMaclispでもTOPS-20でも試してみましたが、ITSPはありませんでした。恐らく名前からして、#+ITSみたいなもんだろうとは思います。

大体見通しが付いたので、細かいOS依存のところは無視して、UNIX上で動くCommon Lisp版といった感じに訳してみました。

(defun rdsyl-ux (filename directory) 
  (prog (ll brakp ans ch fn dir)
        (setq fn (coerce filename 'list))
        (setq dir (coerce directory 'list))
     aa	(setq ll (setq brakp () ))
     a	(setq ch (or (car fn) #\_))	;一文字読みこむ。デフォルトは#\_。#\_は番兵としてループから抜ける判断にも使用される。
        (cond ((or (char= ch #\Nul) (char= ch #\/)) ;読み飛ばす。
	       (pop fn)
	       (setq ch (car fn)))
	      ((char= ch #\[) (setq brakp 't)) ;","文字のエスケープ開始
	      ((char= ch #\]) (setq brakp () )) ;","文字のエスケープ解除
	      ((or (char= ch #\( ) (char= ch #\) )) (return () )) ;括弧は名前に使わないので、nilを返して終了
	      ((char= ch #\,)	        ;エスケープされていない","の場合RETへ飛ぶ。
	       (cond ((not brakp)
		      (pop fn)
		      (go ret))))
	      ((char= ch #\_) (go ret))) ;#\_なら、RETへ飛ぶ。
	(push ch ll)
	(pop fn)
	(go a)
   ret  (setq dir (append dir (nreverse ll)))
	(setq ans (nconc ans (list dir)))
	(and (char= ch #\,) (go aa))
	(return (coerce (car ans) 'string)) ))

動作の解析:

  • ファイル名中の#\Nulと"/"は無視し読み飛ばされる。(UNIX風に変更してみました)
  • ","と"_"はファイル名に使えず、終端文字として機能する。
  • "["が","より先に出現していれば、","は終端文字にならない。
  • ファイル名に"()"は使えない。
(rdsyl-ux "f/o/o.txt" "/tmp/")
=> "/tmp/foo.txt"

のように動作します。

"["と"]"が特別扱いされていますが、これは恐らく、TOPS-10か、SAILのWAITS上でディレクトリが、[30,20]や、[MAC, LSP]の様に表現される為の処理じゃないかと想像しています。ITSは、DSK:DIR;FOO 1のようなファイル名の形式になるので、ITSか、それ以外のOSかを振り分けているのでしょう。

感想:

どうやってループから抜けているのか分からずしばらく悩みました。

多分こういうスタイルにも常套句が色々あって、それに則ったスタイルなんじゃないかなとは思います。

雑感:

それで、結局このコードはGreenblatt氏のコードなのかというと、署名もなにもないのでソースを眺めただけでは分かりません。

Maclispは、JonL White氏がメインで活躍していたみたいなので、JonL氏の可能性も高いと思います。

そして、ループは、progを使用するというスタイルですが、comp.lang.lispでこのコードを紹介したMarshall氏が公開しているLMIの各種Lispマシンのソースコード中のGreenblatt氏のホームディレクトリのLispのファイルを眺めてみてもdoとか普通に使ってるみたいです。ただ70年代の初期のLispマシンの開発においては、progのループでゴリゴリ書いていた可能性は高いとは思います。これは、個人のスタイルというより、70年代全般のスタイルみたいで全体的にdoのループより多いんじゃないかという位です。

それと、Kent Pitman氏のgoタグ使い過ぎの警告についてですが、メッセージを探してみた人がいるみたいですが、見付けられなかったみたいです。自分も暇なので、結構探してみましたが、見付けられませんでした。あるとしたら警告の内容も揶揄するような内容にも思われるので、多分、LMIのライバルのSymbolics社のLispマシンじゃないかなと思います。

ということで、極東の地で壮大に一人でネタに釣られてみました。

2006-06-30

ITSでMACLISP (3)

| 19:01 | ITSでMACLISP (3) - わだばLisperになる を含むブックマーク はてなブックマーク - ITSでMACLISP (3) - わだばLisperになる

MACLISP環境探究の旅は続きます。

さて、MACLISPとEMACSの連携については、LEDITがとい

う支援環境が存在していたということが分かり、少しづ

つではありますが、ITSのMACLISPの開発環境がどんなも

のだったのかが見えて来た気がします。

それで、MACLISP関係についてITSのディレクトリを探索

してみていると、FASLファイルが沢山あるわけなのです

が、これは一体どうやって作成するんだろうなというの

が次なる謎。

FASLファイルとは、FAST LOADの略らしく、コンパイル

されたLispプログラムのファイルの名前で、これを作成

するには、当然ながらコンパイラが必要になると。

とりあえず、MACLISPを起動して、(compile foo)とかし

ても、

(compile foo)
;COMPILE UNDEFINED FUNCTION OBJECT

;BKPT UNDF-FNCTN

となり行き止まり。

コンパイラがなければ、FASLファイルなんかできる筈が

ないので、絶対どっかにあるだろうと思って探ってみた

ら、LISP;-READ- -THIS-ファイルに

Directories with MacLISP relevance are:
	LISP;	-- FASL files
	LSPDMP;	-- Dump files
	L;	-- MIDAS sources for main lisp file, and for others
 	COMLAP;	-- COMpiler (with FASLAP) and LAP assembler
	LSPSRC; -- Various auxillary out-of-core files written in lisp
	NILCOM;	-- More LSPSRC.  Mostly lisp macros, etc.
	LSPMAI;	-- Bug mail and other notices.
	LISP20;	-- Stuff for 20X MacLisp

と記載されていたので、COMLAP;ディレクトリを探って

みると、COMPLR 936を発見。

多分これがコンパイラだと思うので、これをロードして

みることに。

しかし上手く行かないので、COMLAPディレクトリを探索

してみると、CCLOAD 193というファイルがあって、

;;;   ***** Maclisp ****** CCLOAD - Loader for COMPLR **************

という記載があるので、これをロードするのが筋なのか

と、CCLOADをロード。

大量に依存しているFASLファイルがみつかんないと文句

を言われつつ、問題になってるファイルを手動でロード

したり、変数に適当にnilを代入したりして、とりあえ

Initializing
Total Time = 39.5 secs out of 407.8
Dumping LSPDMP;CL.DMP 1137

というところまで辿り着いたけれども、それでコンパイ

ラが使えるようにもなってはいない様子。そして、何故

ダンプして終了してるんだと。

とりあえず謎だらけなので、MACLISPがちゃんと稼働し

てるサイトを参考にしてみることに。

ITSのサイトは、MACLISPウェブサーバとか立ってるので、

あんまり負荷を掛けたら迷惑かなということで、MS創設

者のポール・アレンが主催するサイトであるPDP Planet

のTOPS-20(TOAD-1)の環境をちょっと覗いてみる。

そうそう、その前になぜポール・アレンが、こんなレト

ロコンピュータのサイトを主催したりしてるのかという

と、MS草創期にビル・ゲイツとポール・アレンがALTHEA

用のBASICを書き直した時に使ったりしてたのが、ハー

バード大学計算機センターにあったDECのPDP-10なわけ

ですよ。

で、このBASICがMSの大元になっているわけなので、きっ

とアレンさんはPDP-10に思い入れがあり、かつとんでも

ない大金持ちなので惜しげもなくお金を投入してくれて

るんでしょう。PDP-10の実機を常時動かして、インター

ネットに接続できるように管理するコストは結構凄い金

額だと思います。ありがたや。

申請すれば、ここのマシンのアカウントが貰えるので、

申請してみましょう。いつも大体自分一人しかログイン

してないので、みつけたら声でも掛けてみてください。

閑話休題、全部ひまばなしだけど。

それで、このサイトでは、MACLISPmaclispとして実行

できて<maclisp>ディレクトリに本体があるようなので、

complrを探してみると、COMPLR.EXE.1があった。

EXEということは、COMPLRはもしかして単体で実行でき

る形式になるってことかしらん(TOPS-20の実行形式の拡

張子は、EXEでDOSは、TOPS-20から沢山影響を受けてい

る)、と思い公開ITSサイトでも確認してみたところ、

SYS;TS COMPLRとかがあって、LSPDMP;CL.DMPにリンクし

ているではないか。

なるほど! 自分はコンパイラを作る作業をしていたのか

ということで、LSPDMP;CL.DMPをSYS;TS COMPLRにリンク

してみて実行し、適当なファイルをコンパイルすると、

FASLファイル生成完了。

なるほど! これで、FASLファイルも作れるようになった

ぞと。

そして探索は続く。

2006-06-27

ITSでMACLISP (2)

| 19:12 | ITSでMACLISP (2) - わだばLisperになる を含むブックマーク はてなブックマーク - ITSでMACLISP (2) - わだばLisperになる

MACLISP/ITSの探究を続けています。

とりあえず、MACLISPは使えるようになりましたが、開

発環境としては、どんな感じに整備されていたのかが気

になります。

Lispのインタープリタ内からは、

(load "foo")

等でfoo >(fooというファイルの最新版)が呼び出される

ので、EMACSで、foo >を編集してロードすれば、開発は

できそうなもんですが、これだとインタープリタとエディ

タを行き来しないと行けないので、もっと便利な環境だっ

た筈と思って、ちょっと調べてみたところ、LEDITとい

う支援環境が用意されている様子。

インタープリタから(ledit)でエディタを呼び出し、

EMACSからは、C-xzで戻ってくるってものらしいです。

なるほど。ちなみに、EMACSだけじゃなくて、TECOも使

えます。

それはともかく、MACLISPを起動する度に初期設定する

のが面倒なので、初期設定の方法も探ってみたところ、

LSPDOC;INTRO 24に記述がありました。

サンプルを適当にホームディレクトリに初期化ファイル

を作ってみました。

; MacLisp init file
;;
;; 7:02pm Tuesday,27 June 2006
;; See lspdoc;intro 24

(comment)         ;magic

(PROGN ; real init file stuff
 (ALLOC NIL)
 (SETQ BASE 10. IBASE 10. *NOPOINT NIL) ;change radix
 (SETQ NOUUO T *RSET T) ;these make debugging easier
 (SETQ LEDIT-JNAME 'E)
 '*)            ;return an asterisk for Lisp to print out

(comment)っていうのが大事で、これが無いとちゃんと

読み込んでくれないという謎の記述です。

ALLOCはなんのことだか良く分かってないのですが、nil

にしてみています。

それで、サンプルを見て初めて分かったのですが、出力

と入力の数字の表記は、環境変数のBASEとIBASEで制御

するらしいことが分かりました。10.とすることで、10

進となります。*NOPOINTは、10進の場合に、数字の後に、

点を付けないで表示するかどうかの変数。Tなら、表示

されません。

それと、LEDIT-JNAMEにEを設定していますが、うちは、

EでEMACSが呼び出されるようになっているため。

これで、なんとなく前よりは、環境が整いました!

という感じで、探究は続きます。

2006-06-25

ITSでMACLISP (1)

| 21:32 | ITSでMACLISP (1) - わだばLisperになる を含むブックマーク はてなブックマーク - ITSでMACLISP (1) - わだばLisperになる

あまりにマニアックな内容なので、なんの説明もなしに

つっぱしって逝きます。

SIMHでITSを動かして遊んだりしているわけですが、ITS

で動くLISPの処理系としては、元祖とも言うべき

MACLISPがあります。

それで、SIMHの環境でも当然動きそうなもんなんですが、

lisp^K

と起動させてみようとしても

Alloc? n
.VAL 0; 66745>>HRLM 1,114017   1/   0   114017/   114020

と、Allocに答えたところで終了のようになっていまし

た。(Allocは良く分かんないけども、メモリの割り当て

かなと適当に憶測)

自分が知ってる限りでは、公開ITSサイトは、世界に2サ

イトあるのですが、すんなり起動します。

どっちも、KLH10で動いていて、こっちの環境なら動く

のかなあ、とかぼんやり考える程度で放置していたので

すが、ふと、ジョブを眺めてみると、

*$$v
* LISP P 4

となり、バックグラウンドでは、LISPは動いている様子

なので、$pでフォアグラウンドに持ってきてみたら、

動いてましたヽ(゚∀゚)ノ

適当にテスト

*
(defun fib(n)
(cond((< n 2) n)
(t (+ (fib (1- n)) (fib (- n 2))))))
FIB

(fib 10)
25

あれ?もしかして、8進数表記なのかしら。

10のフィボナッチ数は、55な訳ですが、多分8進数なの

で、10(8進)→8と入力したことになって、25(8進)→21

な訳でしょう。

という訳でちょっとMACLISPを動かして遊んでみようと

いう感じです。

--

SIMH

ITS

MACLISP

KLH10 PDP-10 Emulator