Let's write β

プログラミング中にできたことか、思ったこととか

チューリングマシンのシミュレート

昨日帰省から帰ってきていましたが飛行機の中で特にする事がなく、コンパイラの本を読んですこしつかれたのでコードを書く事にしました。そこでチューリングマシンでもつくってみたらおもしろいだろうなぁとおもい
書いてみたいのが以下です。

;;
;; Infinity Tape
;; 
(defclass <tape> ()
  ((data :initform 
         (make-array 10 
                     :initial-element nil
                     :adjustable t)
         :initarg :data
         :accessor data)
   (look-at :initform 0
            :accessor look-at)))

(defun make-tape-with (list)
  (make-instance '<tape>
                 :data
                 (make-array (length list)
                                :initial-contents list
                                :adjustable t)))

(defmethod get-datum ((tape <tape>))
  (aref (data tape)
        (look-at tape)))

(defmethod set-datum ((tape <tape>) val)
  (setf (aref (data tape)
              (look-at tape)) val))

(defmethod move-left ((tape <tape>))
  (decf (look-at tape))
  ;;underflow tape
  (when (= -1 (look-at tape))
    (setf (data tape)
          (concatenate 'vector 
                       (vector nil)
                       (data tape)))
    (setf (look-at tape) 0)))

(defmethod move-right ((tape <tape>))
  (incf (look-at tape))
  (when (= (length (data tape))
           (look-at tape))
    ;;overflow tape
    (vector-push-extend (data tape) nil)))

;;
;; Turing Machine
;; 
(defclass <machine> ()
  ((input-tape :initform 
               (make-instance '<tape>)
               :initarg :input
               :accessor input-tape)
   (memories :initform
             (vector (make-instance '<tape>))
             :initarg :memories
             :accessor memories)
   (state :initform 'normal
          :initarg :state
          :accessor state)
   (rules :initform ()
          :initarg :rules
          :accessor rules)))

;
; CONSTANT SYMBOL
(defvar *init-state* 'init)
(defvar *error-state* 'error)
(defvar *accept-state* 'accept)

(defvar *blanc-code* 'N)

(defun make-machine (input cnt-memories &optional (rules nil))
  (make-instance '<machine>
                 :input (make-tape-with input)
                 :memories
                 (loop repeat cnt-memories
                       collect (make-instance '<tape>))
                 :state *init-state*
                 :rules rules))

(defmethod lookup-rule ((m <machine>))
  (assoc (state m) (rules m)))

(defmethod update ((m <machine>))
  (if (not (eql *error-state* (state m)))
    (let ((input (get-datum (input-tape m)))
          (rule  (cdr (lookup-rule m))))
      (let ((match-rule (assoc input rule)))
        (if match-rule
          (progn
            (set-datum (input-tape m)
                       (nth 1 match-rule))
            (cond ((eql 'left (nth 2 match-rule))
                   (move-left (input-tape m)))
                  ((eql 'right (nth 2 match-rule))
                   (move-right (input-tape m)))
                  (t
                   (error "Illigal move ~A"
                          (nth 2 match-rule))))
            (setf (state m) (nth 3 match-rule)))
          (error "No match rule"))))
    (error "Error state")))

(defmethod accept-p ((m <machine>))
  (eql *accept-state*
       (state m)))

(defmethod error-p ((m <machine>))
  (eql *error-state*
       (state m)))

(defmethod show-machine-state ((m <machine>))
  (format t "State: ~A~%" (state m))
  (format t "[~A] (~A)~%" (data (input-tape m))
          (look-at (input-tape m)))
  (format t "   ~A^~%"
          (make-string (* (look-at (input-tape m))
                              2)
                       :initial-element #\Space))
  (loop for mem in (memories m)
        do
        (format t "{~A}~%" mem)))

(defparameter *sample-adder*
  (make-machine '(N 1 1 1 0 1 1 1 1 N)
                0
                `((,*init-state*
                    (N N right find-zero))
                  (find-zero
                    (1 1 right find-zero)
                    (0 0 right find-num))
                  (find-num
                    (0 0 left go-head)
                    (1 1 left add))
                  (add
                   (1 0 left shift)
                   (0 0 right add)
                   (N N left go-head))
                  (shift
                    (0 1 right add)
                    (1 1 right go-head))
                  (go-head
                    (N N left accept)
                    (0 0 left go-head)
                    (1 1 left go-head)))))

(defmethod run-machine ((m <machine>) cnt)
  (loop repeat cnt
        until (or (accept-p m) 
                  (error-p m))
        do
        (format t "--------------------~%")
        (show-machine-state m)
        (update m)))

こんな感じで実行されます

CL-TURING(3): (run-machine *sample-adder* 30)
--------------------
State: INIT
[#(N 1 1 1 0 1 1 1 1 N)] (0)
   ^
--------------------
State: FIND-ZERO
[#(N 1 1 1 0 1 1 1 1 N)] (1)
     ^
--------------------
State: FIND-ZERO
[#(N 1 1 1 0 1 1 1 1 N)] (2)
       ^
--------------------
State: FIND-ZERO
[#(N 1 1 1 0 1 1 1 1 N)] (3)
         ^
--------------------
State: FIND-ZERO
[#(N 1 1 1 0 1 1 1 1 N)] (4)
           ^
--------------------
State: FIND-NUM
[#(N 1 1 1 0 1 1 1 1 N)] (5)
             ^
--------------------
State: ADD
[#(N 1 1 1 0 1 1 1 1 N)] (4)
           ^
--------------------
State: ADD
[#(N 1 1 1 0 1 1 1 1 N)] (5)
             ^
--------------------
State: SHIFT
[#(N 1 1 1 0 0 1 1 1 N)] (4)
           ^
--------------------
State: ADD
[#(N 1 1 1 1 0 1 1 1 N)] (5)
             ^
--------------------
State: ADD
[#(N 1 1 1 1 0 1 1 1 N)] (6)
               ^
--------------------
State: SHIFT
[#(N 1 1 1 1 0 0 1 1 N)] (5)
             ^
--------------------
State: ADD
[#(N 1 1 1 1 1 0 1 1 N)] (6)
               ^
--------------------
State: ADD
[#(N 1 1 1 1 1 0 1 1 N)] (7)
                 ^
--------------------
State: SHIFT
[#(N 1 1 1 1 1 0 0 1 N)] (6)
               ^
--------------------
State: ADD
[#(N 1 1 1 1 1 1 0 1 N)] (7)
                 ^
--------------------
State: ADD
[#(N 1 1 1 1 1 1 0 1 N)] (8)
                   ^
--------------------
State: SHIFT
[#(N 1 1 1 1 1 1 0 0 N)] (7)
                 ^
--------------------
State: ADD
[#(N 1 1 1 1 1 1 1 0 N)] (8)
                   ^
--------------------
State: ADD
[#(N 1 1 1 1 1 1 1 0 N)] (9)
                     ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (8)
                   ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (7)
                 ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (6)
               ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (5)
             ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (4)
           ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (3)
         ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (2)
       ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (1)
     ^
--------------------
State: GO-HEAD
[#(N 1 1 1 1 1 1 1 0 N)] (0)
   ^
NIL

文献を参考にして書いたわけではないんので、ちょっと正確な定義かどうかわかりませんが、次は文献を借りてきて修正及び万能チューリングマシンでもつくりたいかなとおもっています。

ランダムドットをつかった立体視画像作成

今日は休み時間に暇だったので、立体視の画像を作成するアルゴリズムを実装し
指定した文字列が見える、指定したサイズの画像を生成する関数を作成しました。

(ql:quickload :vecto)
(ql:quickload :flexi-streams)
(ql:quickload :opticl)

(defun create-random-dot-img (width height)
  (declare (optimize (speed 3) (safety 0)))
  (let ((img (opticl:make-8-bit-gray-image height width)))
    (opticl:fill-image img 0 0 0)
    (loop for y below height
          do
          (loop for x below width
                when (zerop (random 2))
                do
                (setf (opticl:pixel img y x) 
                      (values 255 255 255))))
    img))

(defun calc-font-size (font-loader width height msg)
  (loop for size = 3 then (1+ size)
        for msg-bound = (vecto:string-bounding-box msg size font-loader)
        while (let* ((msg-width (- (aref msg-bound 2) (aref msg-bound 0)))
                     (msg-height (- (aref msg-bound 3) (aref msg-bound 1))))
                (and (<= msg-width width) (<= msg-height height)))
        finally (return (values size msg-bound))))

(defun create-text-img (text width height &optional (padding 0))
  (vecto:with-canvas (:width width :height height)
        (let ((font (vecto:get-font "/usr/share/fonts/truetype/msttcorefonts/Courier_New.ttf")))
          (multiple-value-bind (size bound) (calc-font-size font (- width (* padding 2)) (- height (* padding 2)) text)
            (vecto:set-font font size)
            (vecto:set-rgb-fill 0 0 0)
            (vecto:rectangle 0 0 width height)
            (vecto:fill-path)
            (vecto:stroke)
            (vecto:set-rgb-fill 1.0 1.0 1.0)
            (vecto:set-rgb-stroke 0.5 0.5 0.5)
            (vecto:draw-centered-string (- (/ width 2) (aref bound 0))
                                        (- (/ height 2) (/ (- (aref bound 3) (aref bound 1)) 2))
                                  text)
            (vecto:stroke)
            (with-open-stream (s (flexi-streams:make-in-memory-output-stream 
                                   :element-type '(unsigned-byte 8)))
              (vecto:save-png-stream s)
              (opticl:read-png-stream (flexi-streams:make-in-memory-input-stream 
                                        (flexi-streams:get-output-stream-sequence s))))))))

(defun make-sird-img (text width height &optional (padding 0))
  (let* ((msg-img (create-text-img text width height padding))
         (dot-img-width (ash width -4))
         (dot-img (create-random-dot-img  dot-img-width height))
         (res-img (opticl:make-8-bit-gray-image height width)))
    (loop for x from 0 below width
          for dot-img-x = (mod x dot-img-width)
          for crr-step = (truncate x dot-img-width)
          while (< x width)
          do
          (loop for y from 0 below height
                if (zerop (opticl:pixel msg-img y x))
                do
                (setf (opticl:pixel res-img y x)
                      (opticl:pixel (if (zerop crr-step) dot-img res-img) y
                                    (if (zerop crr-step)
                                      dot-img-x
                                      (+ (* (1- crr-step) dot-img-width) dot-img-x))))
                else
                do
                (loop for shft from 0 upto 4
                      do
                      (setf (opticl:pixel res-img y (- x shft))
                            (opticl:pixel (if (zerop crr-step) dot-img res-img) y
                                          (if (zerop crr-step)
                                            dot-img-x
                                            (+ (* (1- crr-step) dot-img-width) dot-img-x)))))))
    res-img))

こんな感じの画像が生成されます。
f:id:Pocket7878_dev:20121130161639p:plain

KCSって文字が見えましたか?

暇潰しのミニゲームは自作しよう

暇潰しの時にしたいミニゲームがあっても、手元にある事は稀です。
そんなときは、ちょっとしたミニゲームなら自作して遊んでしまうのが一番です。
自分がプレイするのでインターフェイスに凝る必要もないので、簡単です。

糸通し

(ql:quickload :lispbuilder-sdl)
(ql:quickload :lispbuilder-sdl-gfx)
(ql:quickload :lispbuilder-sdl-ttf)

(defvar *font-path* "/usr/share/fonts/truetype/sazanami/sazanami-mincho.ttf")
(defvar *font-size* 30)

(defun get-font ()
  (make-instance 'sdl:ttf-font-definition
                 :size *font-size*
                 :filename (sdl:create-path *font-path*)))

(defclass <pos> ()
  ((x :initform 0 :initarg :x :accessor x)
   (y :initform 0 :initarg :y :accessor y)))

(defclass <area> (<pos>)
 ((y2 :initform 0 :initarg :y2 :accessor y2)))

(defmethod collision-p ((pos <pos>) (area <area>))
  (and (= (x pos) (x area))
       (<= (y area) (y pos) (y2 area))))

(defun gen-random-hari (xpos)
  (let ((random-y (+ 160 (random 75)))
	(random-hole-size (+ 20 (random 10))))
  (make-instance '<area>
     :x xpos :y random-y :y2 (+ random-hole-size random-y))))

(defun ito ()
  (let* ((ito-pos (make-instance '<pos>
                      :x 300 :y 240))
	 (hari-list (list (gen-random-hari 480)))
 	(hari-speed 1)
	 (score 0)
	 (curr-hari (car hari-list)))
  (sdl:with-init ()
    (sdl:window 600 480 :title-caption "Ito")
    (sdl:clear-display sdl:*black*)
    (sdl:enable-key-repeat 1 1)
    (sdl:initialise-default-font (get-font))
    (sdl:with-events ()
      (:quit-event () t)
      (:key-down-event ()
        (decf (y ito-pos) (1+ hari-speed)))
      (:idle ()
	(sdl:clear-display sdl:*black*)
	(sdl-gfx:draw-circle-* (x ito-pos) (y ito-pos) 3 :color sdl:*white*)
	(sdl-gfx:draw-string-solid-* (format nil "Score: ~A" score) 400 30)
	(loop for hari in hari-list
		do
		(sdl-gfx:draw-box-* (x hari) (- (y hari) 10)
				    5
				    (- 480 (y hari) 10) :color sdl:*white*)
		(sdl-gfx:draw-box-* (x hari) (y hari) 5 (- (y2 hari) (y hari))
		 :color sdl:*blue*))
	(incf (y ito-pos) hari-speed)
	(loop for hari in hari-list
		do
		(decf (x hari) hari-speed))
	(when (= (x curr-hari) 300)
	  (if (not (collision-p ito-pos curr-hari))
		(sdl:push-quit-event)
		(progn (push (gen-random-hari 480) hari-list)
			(incf score 5)
			(setf hari-speed (1+ (truncate score 30)))
			(setf curr-hari (car hari-list)))))
        (sdl:update-display))))))

Touch the numbers

(ql:quickload :lispbuilder-sdl)
(ql:quickload :lispbuilder-sdl-gfx)
(ql:quickload :lispbuilder-sdl-ttf)

(defun iota (num)
 (loop for n from 1 upto num
	collect n))

(defun random-map ()
 (let ((num-list (iota 25))
       (game-map (make-array '(5 5))))
  (loop for y from 0 upto 4
	do
	(loop for x from 0 upto 4
		do
		(let ((item (nth (if (= 1 (length num-list))
					0
					(random (length num-list))) num-list)))
		  (setf (aref game-map y x) (cons item nil))
			(setf num-list (remove item num-list)))))
	game-map))

(defvar *font-path* "/usr/share/fonts/truetype/sazanami/sazanami-mincho.ttf")
(defvar *font-size* 30)

(defun get-font (size)
  (make-instance 'sdl:ttf-font-definition
                 :size size
                 :filename (sdl:create-path *font-path*)))

(defparameter *btn-size* 100)

(defun game-end-p (map)
  (loop for y from 0 upto 4
       always
       (loop for x from 0 upto 4
	    always (cdr (aref map y x)))))


(defun display-map (map)
  (loop for y from 0 upto 4
       do
       (loop
	  for x from 0 upto 4
	  for btn = (aref map y x)
	  if (null (cdr btn))
	  do
	    (sdl:draw-box-*
	     (+ 5 (* *btn-size* x))
	     (+ 5 (* *btn-size* y)) (- *btn-size* 5)
	     (-  *btn-size* 5)
	     :color sdl:*blue*)
	  else
	  do
	    (sdl:draw-box-*
	     (+ 5 (* *btn-size* x))
	     (+ 5 (* *btn-size* y)) (- *btn-size* 5)
	     (-  *btn-size* 5)
	     :color sdl:*red*)
	  do
	    (sdl:draw-string-solid-*
	     (format nil "~D" (car (aref map y x)))
	     (+ 30 (* *btn-size* x))
	     (+ 30 (* *btn-size* y))
	     :color sdl:*white*))))

(defun sec-to-time-string (sec)
  (let ((minutes (truncate sec 60))
	(rest-sec (mod sec 60)))
    (format nil "~D:~2,'0,,D" minutes rest-sec)))

(defun numclick ()
  (let ((my-map (random-map))
	(start-time (get-universal-time))
	(end-time)
	(curr-num 0))
    (sdl:with-init ()
      (sdl:window (+ 5 (* *btn-size* 5))
		  (+ 5 (* *btn-size* 5))
		  :title-caption "Num Click")
      (sdl:clear-display sdl:*black*)
      (sdl:initialise-default-font (get-font 30))
      (sdl:with-events ()
	(:quit-event () t)
	(:mouse-button-down-event (:x mouse-x :y mouse-y)
	    (let* ((btn-x (truncate mouse-x *btn-size*))
		   (btn-y (truncate mouse-y *btn-size*))
		   (btn-num (car (aref my-map btn-y btn-x))))
	      (when (= (1+ curr-num) btn-num)
		(incf curr-num)
		(setf (cdr (aref my-map btn-y btn-x)) t))))
	(:idle ()
	       (sdl:clear-display sdl:*black*)
	       (display-map my-map)
	       (when (and (null end-time) (game-end-p my-map))
		 (setf end-time (get-universal-time)))
	       (when (not (null end-time))
		 (sdl:with-font (f (get-font 50))
		   (sdl:draw-string-solid-* (sec-to-time-string (- end-time start-time))
					    30 250
					    :font f)))
	       (sdl:update-display))))))

GCの可視化をしようとしている。

GCでのメモリの様子などを可視化するとおもしろいかもなぁとおもい
とりあえず一時間ほどで書いてみました。Mark&Sweepをしています。(かなり初歩的な部分しかしていませんが)

(defclass <lobject> ()
  ((mark :initform nil :initarg :mark :accessor mark)
   (next :initform nil :initarg :next :accessor next)))

(defclass <cons> (<lobject>)
  ((car-ref :initarg :car-ref :accessor car-ref)
   (cdr-ref :initarg :cdr-ref :accessor cdr-ref)))

(defun create-cons (car-lobj cdr-lobj)
  (make-instance '<cons>
                 :car-ref car-lobj
                 :cdr-ref cdr-lobj))

(defclass <atom> (<lobject>)
  ((val :initform nil :initarg :val :accessor val)))

(defparameter *max-atom* 25)
(defparameter *max-cons* 25)

(defclass <bind-table> ()
  ((bind-table :initform nil :accessor bind-table)))

(defmethod bind ((table <bind-table>) (symb Symbol) (lobj <lobject>))
  (push
    (cons symb lobj)
    (bind-table table)))

(defmethod unbind ((table <bind-table>)
                   (symb Symbol))
  (setf (bind-table table)
        (remove-if (lambda (bind)
                     (eql (car bind) symb))
                   (bind-table table))))

(defmethod lookup ((table <bind-table>) (symb Symbol))
  (assoc symb (bind-table table)))

(defclass <lisp-vm> ()
  ((free-atoms :initform 
               (make-array *max-atom*
                           :initial-contents
                           (loop repeat *max-atom*
                                 for new-atom = (make-instance '<atom>)
                                 collect new-atom))
               :accessor free-atoms)
   (free-cells :initform
               (make-array *max-cons*
                           :initial-contents
                           (loop repeat *max-cons*
                                 for new-cons = (make-instance '<cons>)
                                 collect new-cons))
                           :accessor free-cells)
   (bind-table :initform 
               (make-instance '<bind-table>)
               :accessor bind-table)
   (free-cell-top :accessor free-cell-top)
   (free-cell-bottom :accessor free-cell-bottom)
   (free-atom-top :accessor free-atom-top)
   (free-atom-bottom :accessor free-atom-bottom)))

(defmethod init-vm ((vm <lisp-vm>))
  (with-slots (free-cells free-atoms
                  free-cell-top free-cell-bottom
                  free-atom-top free-atom-bottom) vm
    ;;Initialize free area
    (init-free-cells free-cells)
    (init-free-atoms free-atoms)
    (setf (bind-table (bind-table vm)) nil)
    ;;Setup pointers
    (setf free-cell-top (aref free-cells 0))
    (setf free-cell-bottom (aref free-cells 
                                 (1- (length free-cells))))
    (setf free-atom-top (aref free-atoms 0))
    (setf free-atom-bottom (aref free-atoms
                                 (1- (length free-atoms))))))

(defun init-free-cells (cells)
  (loop for idx from 0 upto (- (length cells) 2)
        for prev-cell = (aref cells idx)
        for next-cell = (aref cells (1+ idx))
        do
        (setf (car-ref prev-cell) nil
              (cdr-ref prev-cell) nil
              (next prev-cell) next-cell)
  (setf (next (aref cells (1- (length cells)))) nil)))

(defun init-free-atoms (atoms)
  (loop for idx from 0 upto (- (length atoms) 2)
        for prev-atom = (aref atoms idx)
        for next-atom = (aref atoms (1+ idx))
        do
        (setf (val prev-atom) nil
              (next prev-atom) next-atom)
  (setf (next (aref atoms (1- (length atoms)))) nil)))


(defun create-vm ()
  (let ((vm (make-instance '<lisp-vm>)))
    (init-vm vm)
    vm))

(defmethod alloc-cons ((vm <lisp-vm>))
  ;;まずフリーリストの先頭を取得
  (let ((new-cons (free-cell-top vm)))
    (if (not (null new-cons))
      (progn
        ;;フリーリストを一つすすめる
        (setf (free-cell-top vm)
            (next (free-cell-top vm)))
        (setf (car-ref new-cons) nil
              (cdr-ref new-cons) nil)
        new-cons)
      (progn
        (run-gc vm)
        (if (not (null (free-cell-top vm)))
          (alloc-cons vm)
          (error "Can't allocate new cons.."))))))

(defmethod alloc-atom ((vm <lisp-vm>))
  (let ((new-atom (free-atom-top vm)))
    (if (not (null new-atom))
      (progn
        (setf (free-atom-top vm)
            (next (free-atom-top vm)))
        (setf (val new-atom) nil)
        new-atom)
      (progn
        (run-gc vm)
        (if (not (null (free-atom-top vm)))
          (alloc-atom vm)
          (error "Can't allocate new atom.."))))))
;;;;;;;;;;;;;;;;;;;;;;;;;
;; 
;;  Mark
;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod recur-mark ((obj <lobject>))
  (error "Unknown object"))

(defmethod recur-mark ((cell <cons>))
  (setf (mark cell) t)
  (when (car-ref cell)
    (recur-mark (car-ref cell)))
  (when (cdr-ref cell)
    (recur-mark (cdr-ref cell))))

(defmethod recur-mark ((atom <atom>))
  (setf (mark atom) t))

(defmethod mark-phrase ((vm <lisp-vm>))
  (loop for bind in (bind-table (bind-table vm))
        do
        (recur-mark (cdr bind))))

(defmethod display-vm-state ((vm <lisp-vm>))
  (princ "Cells: ")
  (loop for cell across (free-cells vm)
        do
        (cond ((mark cell) (format t "*"))
              ((equal (free-cell-top vm) cell)
               (format t "F"))
              ((equal (free-cell-bottom vm) cell)
               (format t "B"))
              (t (format t "-")))
        finally (format t "~%"))
  (princ "Atoms: ")
  (loop for atom across (free-atoms vm)
        do
        (cond ((mark atom) (format t "*"))
              ((equal (free-atom-top vm) atom)
               (format t "F"))
              ((equal (free-atom-bottom vm) atom)
               (format t "B"))
              (t (format t "-")))
        finally (format t "~%"))
  (format t "Bind: ~%~{|~A|~%~}~%" (bind-table (bind-table vm))))

(defmethod bind-val ((vm <lisp-vm>) (sym Symbol) (lobj <lobject>))
  (bind (bind-table vm) sym lobj))

;;;;;;;;;;;;;;;;;
;;
;; Sweep
;;
;;;;;;;;;;;;;;;;;
(defmethod sweep-cells ((vm <lisp-vm>))
  ;;setup free list
  (loop for cell across (free-cells vm)
        until (not (mark cell))
        finally (setf (free-cell-top vm) cell
                      (free-cell-bottom vm) cell))
  (loop for cell across (free-cells vm)
        if (not (mark cell))
        do
        (setf (next (free-cell-bottom vm)) cell)
        (setf (free-cell-bottom vm) cell)
        else
        do
        (setf (mark cell) nil)
        finally (setf (next (free-cell-bottom vm)) nil)))

(defmethod sweep-atoms ((vm <lisp-vm>))
  ;;setup free list
  (loop for atom across (free-atoms vm)
        until (not (mark atom))
        finally (setf (free-atom-top vm) atom
                      (free-atom-bottom vm) atom))
  (loop for atom across (free-atoms vm)
        if (not (mark atom))
        do
        (setf (next (free-atom-bottom vm)) atom)
        (setf (free-atom-bottom vm) atom)
        else
        do
        (setf (mark atom) nil)
        finally (setf (next (free-atom-bottom vm)) nil)))

(defmethod sweep-phrase ((vm <lisp-vm>))
    (sweep-atoms vm)
    (sweep-cells vm))

;;;;
;;; GC
;;; 
(defmethod run-gc ((vm <lisp-vm>))
  (format t "[Garbage collection]~%");
  (mark-phrase vm)
  (sweep-phrase vm))

(defmethod gc-test ((vm <lisp-vm>))
  (loop repeat (+ *max-cons* 5)
        do
        (display-vm-state vm)
        (if (zerop (random 5))
          (bind-val vm (gensym) (alloc-cons vm))
          (alloc-cons vm))))

(defmethod primitive-p (val)
  (or (numberp val)
      (stringp val)))

(defmacro aif (test then else)
  `(let ((it ,test))
     (if it
       ,then
       ,else)))

(defmethod leval ((vm <lisp-vm>) expr)
  (cond ((primitive-p expr)
         (let ((new-atom (alloc-atom vm)))
           (setf (val new-atom) expr)
           new-atom))
        ((symbolp expr)
         (aif (lookup (bind-table vm) expr)
           (cdr it)
           (error "Undefined variable")))
        ((and (eql 'bind (car expr))
              (symbolp (cadr expr)))
         (bind-val vm (cadr expr) 
                   (leval vm (caddr expr))))
        ((and (eql 'cons (car expr))
              (= 2 (length (cdr expr))))
         (let ((new-cons (alloc-cons vm)))
           (setf (car-ref new-cons) (leval vm (cadr expr))
                 (cdr-ref new-cons) (leval vm (caddr expr)))
           new-cons))
        (t (error "Unrecognizable expr"))))

こんな感じです。

CL-USER(2): (defparameter *sample-vm* (create-vm))

*SAMPLE-VM*
CL-USER(3): (display-vm-state *sample-vm*)
Cells: F-----------------------B
Atoms: F-----------------------B
Bind: 

NIL

CL-USER(4): (leval *sample-vm* '(cons 10 10))

#<<CONS> {B6C1B19}>
CL-USER(5): (display-vm-state *sample-vm*)
Cells: -F----------------------B
Atoms: --F---------------------B
Bind: 

NIL

CL-USER(6): (leval *sample-vm* '(bind x 10))

((X . #<<ATOM> {B65E229}>))
CL-USER(7): (display-vm-state *sample-vm*)
Cells: -F----------------------B
Atoms: ---F--------------------B
Bind: 
|(X . #<<ATOM> {B65E229}>)|

NIL

CL-USER(8): (mark-phrase *sample-vm*)

NIL
CL-USER(9): (display-vm-state *sample-vm*)
Cells: -F----------------------B
Atoms: --*F--------------------B
Bind: 
|(X . #<<ATOM> {B65E229}>)|

NIL

CL-USER(10): (sweep-phrase *sample-vm*)

NIL
CL-USER(11): (display-vm-state *sample-vm*)
Cells: F-----------------------B
Atoms: F-----------------------B
Bind: 
|(X . #<<ATOM> {B65E229}>)|

NIL

CL-USER(12): (leval *sample-vm* '(bind y 20))

((Y . #<<ATOM> {B65E1C9}>) (X . #<<ATOM> {B65E229}>))
CL-USER(13): (leval *sample-vm* '(bind z (cons x y)))

((Z . #<<CONS> {B6C1B19}>) (Y . #<<ATOM> {B65E1C9}>) (X . #<<ATOM> {B65E229}>))
CL-USER(14): (display-vm-state *sample-vm*)
Cells: -F----------------------B
Atoms: -F----------------------B
Bind: 
|(Z . #<<CONS> {B6C1B19}>)|
|(Y . #<<ATOM> {B65E1C9}>)|
|(X . #<<ATOM> {B65E229}>)|

NIL

CL-USER(15): (mark-phrase *sample-vm*)

NIL
CL-USER(16): (display-vm-state *sample-vm*)
Cells: *F----------------------B
Atoms: *F*---------------------B
Bind: 
|(Z . #<<CONS> {B6C1B19}>)|
|(Y . #<<ATOM> {B65E1C9}>)|
|(X . #<<ATOM> {B65E229}>)|

NIL
CL-USER(17): (run-gc *sample-vm*)
[Garbage collection]
NIL
CL-USER(18): (display-vm-state *sample-vm*)
Cells: -F----------------------B
Atoms: -F----------------------B
Bind: 
|(Z . #<<CONS> {B6C1B19}>)|
|(Y . #<<ATOM> {B65E1C9}>)|
|(X . #<<ATOM> {B65E229}>)|

NIL

こんな感じでmarkされてsweepされている様子があるていど確認できます。

バイナリサーチ

(defun kv (key val)
  (cons key val))

(defun make-table (&rest kv-pairs)
  (make-array (length kv-pairs)
              :initial-contents kv-pairs))

(defun aridx1 (array idx)
  (aref array (1- idx)))

(defun bin-search (key table)
  (let ((lo 1)
        (hi (length table)))
    (loop while (<= lo hi)
          do
          (let ((mid (ash (+ lo hi) -1)))
            (format t "lo: ~A hi: ~A mid: ~A~%" lo hi mid)
            (when (<= key (car (aridx1 table mid)))
              (setf hi (1- mid)))
            (when (>= key (car (aridx1 table mid)))
              (setf lo (1+ mid)))))
    (if (= lo (+ hi 2))
      (values t (1- lo))
      nil)))

(defun %bin-search (key table)
  (let ((lo 1)
        (hi (length table)))
    (loop while (<= lo hi)
          do
          (let ((mid (ash (+ lo hi) -1)))
            (format t "lo: ~A hi: ~A mid: ~A~%" lo hi mid)
            (if (< key (car (aridx1 table mid)))
              (setf hi (1- mid))
              (setf lo (1+ mid)))))
    (if (zerop hi)
      nil
      (values (= key (car (aridx1 table hi))) hi))))

二分探索木

(defun kv (key val)
  (cons key val))

(defun tree (val left right)
  (list val left right))

(defun tree->val (tree)
  (car tree))

(defun tree->left (tree)
  (cadr tree))

(defun tree->right (tree)
  (caddr tree))

(defun leafp (tree)
  (and (null (tree->left tree))
       (null (tree->right tree))))

(defun walk (tree)
  (cond ((null tree) nil)
        ((leafp tree)
         (format t "Visit: ~A~%" (tree->val tree)))
        (t
         (progn
           (walk (tree->left tree))
           (format t "Visit: ~A~%" (tree->val tree))
           (walk (tree->right tree))))))

(defun tree-search (val tree)
  (cond 
    ((null tree) nil)
    ((= val (tree->val tree))
     tree)
    (t
     (if (< val (tree->val tree))
       (tree-search val (tree->left tree))
       (tree-search val (tree->right tree))))))

(defun %tree-search (val tree)
  (let ((search nil))
    (loop while (not (null tree))
          do
          (if (= val (tree->val tree))
            (progn
              (setf search tree)
              (setf tree nil))
            (if (< val (tree->val tree))
              (setf tree (tree->left tree))
              (setf tree (tree->right tree)))))
    search))

一次元セルオートマトンギャラリー

(ql:quickload :lispbuilder-sdl)
(ql:quickload :lispbuilder-sdl-gfx)

(defclass <world> ()
    ((cells :initform () :initarg :cells :accessor cells)
     (rules :initform () :initarg :rules :accessor rules)))

(defun make-world (len)
  (make-instance '<world>
		 :cells (let ((lst (make-list len :initial-element 0)))
			  (setf (nth (/ len 2) lst) 1)  lst)))

(defun neighbors (list idx)
  (cond ((zerop idx)
         `(0 ,(nth idx list) ,(nth 1 list)))
        ((= (1- (length list)) idx)
         `(,(nth (1- idx) list) ,(nth idx list) 0))
        (t
         `(,(nth (mod (1- idx) (length list)) list)
            ,(nth idx list)
            ,(nth (mod (1+ idx) (length list)) list)))))

(defun dispatch-rule (list rules)
  (let ((matched-rule 
          (find list rules :test 'equal :key #'car)))
    (if matched-rule
      (cdr matched-rule)
      0)))

(defmethod update-world ((world <world>))
  (setf (cells world)
        (loop
          for idx from 0 upto (length (cells world))
          collect (dispatch-rule 
                    (neighbors (cells world) idx) 
                    (rules world))))
  world)

(defmethod show-world-cui ((world <world>))
  (format t "~A~%" (cells world)))

(defun fill-list (len list stuff)
  (if (< (length list) len)
    (append (make-list (- len (length list))
                       :initial-element stuff)
            list)
    list))

(defun dec-to-bin-list (num)
  (labels ((%dec-to-bin-list (num acc)
             (if (zerop num)
               acc
               (%dec-to-bin-list 
                 (truncate num 2)
                 (cons (mod num 2) acc)))))
    (if (zerop num) '(0) 
      (%dec-to-bin-list num ()))))

(defun make-random-rule ()
  (loop for x from 0 upto 7
        for pat = (fill-list 3 (dec-to-bin-list x) 0)
        for rule = (random 2)
        collect (cons pat rule)))

(defun make-random-world (len)
  (let ((world (make-world len)))
    (setf (rules world) (make-random-rule))
    world))

(defun create-rule (rule-list)
  (loop for x from 0 upto 7
        for pat = (fill-list 3 (dec-to-bin-list x) 0)
        for rule in rule-list
        collect (cons pat rule)))

(defparameter *cell-size* 20)
(defparameter *max-gen* 30)

(defmacro each-with-idx (list fn)
  (let ((idx (gensym)))
    `(loop for ,idx from 0 upto (1- (length ,list))
           collect (funcall ,fn (nth ,idx ,list) ,idx))))

(defmethod clone-world ((world <world>))
  (make-instance '<world>
                 :cells (cells world)
                 :rules (rules world)))

(defun life-saver (len)
  (let ((world (make-random-world len))
        (alpha 255)
        (width (* len *cell-size*))
        (height (* *cell-size* *max-gen*))
        (state :display))
    (sdl:with-init ()
      (sdl:window width height :title-caption "Life Saver")
      (sdl:clear-display sdl:*blue*)
      (sdl:with-events ()
        (:quit-event () t)
        (:idle
          (cond ((eql state :display)
                 (progn
                   (sdl:clear-display sdl:*blue*)
                   (loop for gen from 0 upto *max-gen*
                         do
                         (progn
                           (show-world-cui world)
                           (each-with-idx (cells world)
                                          (lambda (cell idx) (when (not (zerop cell))
                                                               (sdl-gfx:draw-box-*
                                                                 (* *cell-size* idx)
                                                                 (* *cell-size* gen)
                                                                 *cell-size* *cell-size* 
                                                                 :color (sdl:color :r alpha :g 255 :b 255 :a alpha)))))
                           (update-world world)))
                   (setf state :changing)))
                ((eql state :changing)
                   (if (zerop alpha)
                     (setf world (make-random-world len) state :display alpha 255)
                     (decf alpha 5))))
            (sdl:update-display))))))

ランダムなパターンを生成して表示し、一定時間がたったらまた別のパターンを生成し表示してといやっておいます。
alphaのあたりはとりあえず無視しておいてください。フェードアウトさせようとおもっていた頃の名残りでので。

円形グラフプロット

角度と値を元にした円形のグラフを書く必要がある場面があったので、
とりあえずの出力結果を確認するためにlispbuilder-sdlをつかって書いてみました。

(ql:quickload :lispbuilder-sdl)
(ql:quickload :lispbuilder-sdl-gfx)

(defun read-plot-data (file-name)
  (with-open-file (in file-name :direction :input
                      :if-does-not-exist nil) 
    (loop for deg = (read in nil)
          for val = (read in nil)
          while (and deg val)
          collect (cons deg val))))

(defun deg->rad (deg)
  (* COMMON-LISP:PI (/ deg 180)))

(defun circle-graph (plot-data)
  (sdl:with-init ()
   (sdl:window 400 400)
   (sdl:clear-display sdl:*white*)
     (sdl:with-events ()
       (:quit-event () t)
        (:idle ()
         ;;目盛を書く
         (loop for radius from 10 upto 200 by 10
               do
               (sdl-gfx:draw-circle-* 200 200 radius :color sdl:*black*))
         (loop for deg from 0 upto 360 by 10
               do
               (sdl-gfx:draw-line-* 200 200 
                    (round 
                      (+ 200 (* 200 (cos (deg->rad deg)))))
                    (round 
                      (- 200 (* 200 (sin (deg->rad deg)))))
                    :color sdl:*black*))
         (loop for data in plot-data
               for deg = (car data)
               for val = (cdr data)
               for x = (round 
                         (+ 200 (* 10 val (cos (deg->rad deg)))))
               for y = (round
                         (- 200 (* 10 val (sin (deg->rad deg)))))
               do
                (sdl-gfx:draw-filled-circle-* x y 3 :color sdl:*red*))
         (sdl:update-display)))))

かなり適当に何も考慮していない実装になっているような気がします。

0 20
15 40
30 10
45 13
50 34

こんなデータにたいして

(circle-graph (read-plot-data "plot.data"))

こんな感じでよんでやると
f:id:Pocket7878_dev:20121028002328p:plain
こんな感じで表示されます。
Vectoで描画して画像にしたほうが綺麗に表示されるような気がするので、後ほどやってみます。

追記:
Vectoにしてみました。

(defun circle-graph-png (plot-data out-file)
  (vecto:with-canvas (:width 400 :height 400)
    (vecto:translate 200 200)
    ;;目盛を書く
    (vecto:move-to 0 0)
    (loop for radius from 10 upto 200 by 10
          do
          (vecto:centered-circle-path 0 0 radius))
    (vecto:stroke)
    (loop for deg from 0 upto 360 by 10
          do
          (vecto:move-to 0 0)
          (vecto:line-to 
            (* 200 (cos (deg->rad deg)))
            (* 200 (sin (deg->rad deg))))
          (vecto:stroke))
    ;;データを書く
    (vecto:set-rgb-fill 1.0 0.0 0.0)
    (loop for data in plot-data
          for deg = (car data)
          for val = (cdr data)
          do
          (vecto:centered-circle-path
            (* 10 val (cos (deg->rad deg)))
            (* 10 val (sin (deg->rad deg))) 3)
          (vecto:fill-path)
          (vecto:stroke))
    (vecto:save-png out-file)))

画像は、
f:id:Pocket7878_dev:20121028002130p:plain
こうなります。モワレェ...

SBCLのdisassembleの結果を比較する

SBCLでdisassembleを用いてアセンブラ(?)を出力した時に番地などの詳細情報がでてきて
diffをとって比較するのが困難なので、ちょっと必要な部分だけを抽出してくれるawkスクリプトを書いてみました。

{
	if(NF >= 5 && $3 ~ /.*:/) {
			print $3,$5,$6,$7
	} else {
		if($6 ~ /;/) {
			print $4,$5
		} else {
			print $4,$5,$6
		}
	}
}

ためしに通してみると

; disassembly for HOGE
; 02C7E279:       488B55F8         MOV RDX, [RBP-8]           ; no-arg-parsing entry point
;       7D:       BF08000000       MOV EDI, 8
;       82:       488D0C25AF040020 LEA RCX, [#x200004AF]      ; GENERIC-=
;       8A:       FFD1             CALL RCX
;       8C:       7508             JNE L1
;       8E:       31D2             XOR EDX, EDX
;       90: L0:   488BE5           MOV RSP, RBP
;       93:       F8               CLC
;       94:       5D               POP RBP
;       95:       C3               RET
;       96: L1:   488B55F8         MOV RDX, [RBP-8]
;       9A:       31FF             XOR EDI, EDI
;       9C:       488D0C25AF040020 LEA RCX, [#x200004AF]      ; GENERIC-=
;       A4:       FFD1             CALL RCX
;       A6:       BA00000000       MOV EDX, 0
;       AB:       41BB08000000     MOV R11D, 8
;       B1:       490F44D3         CMOVEQ RDX, R11
;       B5:       EBD9             JMP L0
;       B7:       CC0A             BREAK 10                   ; error trap
;       B9:       02               BYTE #X02
;       BA:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       BB:       54               BYTE #X54                  ; RCX

HOGE  
MOV RDX, [RBP-8]
XOR EDI, EDI
LEA RCX, [#x200004AF]
CALL RCX 
JEQ L1 
XOR EDX, EDX
L0: MOV RSP, RBP
CLC  
POP RBP 
RET  
L1: MOV RDX, [RBP-8]
MOV EDI, 8
LEA RCX, [#x200004AF]
CALL RCX 
MOV EDX, 8
MOV R11D, 0
CMOVEQ RDX, R11
JMP L0 
BREAK 10
BYTE #X02 
BYTE #X18
BYTE #X54

という風に出力されます。

CLでmetaclassでdispatchするdefmethodはできない?

素朴な疑問なのですが、CommonLispでdefmethodするにあたって、meta-classレベルでdispatchできるのでしょうか?
http://coding.derkeiler.com/Archive/Lisp/comp.lang.lisp/2007-06/msg01042.html
ここを見るかぎり、できないとの回答でした。
しかし、Postmodernなど、metaclassを提供しているライブラリもあり、そんなライブラリから自分のクラスを拡張しているとmetaclassレベルでディスパッチしたい事もあります。
たとえば、PostmodernはDBにStoreできるクラスのためのメタクラスとしてdao-classというメタクラスを提供しているのですが、このdao-classをmetaclassとするクラスにたいする共通のテンプレートメソッドのようなものを定義したいときがあります。
まぁ自分のためのツールなら、自分で気をつければ済む話なのではありますが、ほしい機構ではあります。
ちなみに、dao-classをmetaclassとするクラスに提供されているdao-classにはsave-dao等の共通機能があります。
そちらは

(defun save-dao (dao)
  "Try to insert the content of a DAO. If this leads to a unique key
violation, update it instead."
  (handler-case (progn (insert-dao dao) t)
    (cl-postgres-error:unique-violation ()
      (update-dao dao)
      nil)))

となっています。なんらdispatchしてないので、当然普通のインスタンスにたいしても、呼べはします。(insert-daoが未定義といって落ちますが)

そこで、insert-daoを見てみると

(defun build-dao-methods (class)
  .....
 (defmethod insert-dao ((object ,class))
        (let (bound unbound)
         ....
 )

となっています。つまり、直接クラスにメソッドをインストールしているようなのです。

何か良いほうほうはないでしょうか、コメントおまちしています。

ダメだった..

できると、おもっていた.できなかった。

(defpartial main-layout (&rest contents)
 (:html
   (:head
     (:link :href "/static/css/bootstrap.css" :rel "stylesheet" :type "text/css")
     (:script :src "/static/js/bootstrap.min.js"))
   (:body
     contents)))

CL-APIDOC(28): (main-layout "hoge" `(:hoge "hoge"))

"<html><head><link href=\"/static/css/bootstrap.css\" rel=\"stylesheet\" type=\"text/css\" /><script src=\"/static/js/bootstrap.min.js\" /></head><body>(hoge (HOGE hoge))</body></html>"

...
Clojureのnoirなら..(hiccup)

(defpartial layout [& content]
            (html5
              [:head]
              [:body
               [:div#wrapper
                content]]))

(layout
  [:p "Hoge"]
  [:h2 "Hoge"])

とできる...まぁclojureにはリスト以外にもリスト風に記述できる物があって、しかもそれ自体がデータ型なので、それを処理できるのでしょう..
ちょっと悔やしいので、どなたか良い方法をおしえてください..