2016年3月22日火曜日

[SICP][Lisp]ストリーム

SICP 3.5 ストリーム Common Lisp で実装したので、コードを貼っておこう。

遅延ストリーム・無限ストリームを使って、エラトステネスのふるいを作成し、素数を求めた。

(defmacro stream-cons (a b)
  `(cons ,a
  (delay ,b)))

(defun stream-car (s)
  (car s))

(defun stream-cdr (s)
  (force (cdr s)))

(defmacro delay (func)
  `#'(lambda () ,func))

(defun force (delay-obj)
  (funcall delay-obj))

(defvar +stream-empty+ (delay nil))

(defun stream-null? (s)
  (eq s +stream-empty+))

(defun stream-enumerate-interval (a b)
  (format t "[~a]~%" a b)
  (if (= a b)
      +stream-empty+
      (stream-cons a
     (stream-enumerate-interval (1+ a) b))))

(defun stream-each (func s)
  (unless (stream-null? s)
    (funcall func (stream-car s))
    (stream-each func (stream-cdr s))))

(defun stream-cadr (s)
  (stream-car (stream-cdr s)))

(defun stream-caddr (s)
  (stream-car (stream-cdr (stream-cdr s))))

(defun stream-nth (n s)
  (if (= n 0)
      (stream-car s)
      (stream-nth (1- n) (stream-cdr s))))

(defun stream-integers-starting-from (n)
  (stream-cons n
        (stream-integers-starting-from (1+ n))))

(defun stream-map (func &rest ss)
  (if (null (car ss))
      +stream-empty+
      (stream-cons
       (apply func (mapcar #'car ss))
       (apply #'stream-map (cons func (mapcar #'stream-cdr ss))))))

(defun stream-filter (pred s)
  (if (stream-null? s)
      +stream-empty+
      (if (funcall pred (car s))
   (stream-cons (car s)
         (stream-filter pred (stream-cdr s)))
   (stream-filter pred (stream-cdr s)))))

(defun stream-take (s n)
  (labels ((iter (s ts n)
      (if (= n 0)
   (reverse ts)
   (iter (stream-cdr s)
         (cons (stream-car s) ts)
         (1- n)))))
    (iter s '() n)))

(defun divisible? (x y)
  (= (mod x y) 0))

(defun sieve (stream)
  (stream-cons
   (stream-car stream)
   (sieve
    (stream-filter
     #'(lambda (x) (not (divisible? x (stream-car stream))))
     (stream-cdr stream)))))

sieve関数に2から始まる整数の無限ストリームを渡すと、最初の要素の値2と、2で割れる数を除外した無限ストリームを引数とするsieve関数の結果をconsしたものとなり、素数を表す無限ストリームが得られる。


実行例

最初の10個の素数を取得する。

CL-USER> (stream-take (sieve (stream-integers-starting-from 2)) 10) 
(2 3 5 7 11 13 17 19 23 29)
おおっ。素晴しい。

1000個目の素数を取得する。

CL-USER> (stream-nth 999 (sieve (stream-integers-starting-from 2)))
7919

楽しいねー。

 

10000個目の素数を取得する。

CL-USER> (stream-take (sieve (stream-integers-starting-from 2)) 9999) 
帰ってこない...

と思ったら、SBCL(swankサーバー側)でエラーとなっていて、 ヒープを使い尽していた。

fatal error encountered in SBCL pid 26815(tid 140737295218432):
Heap exhausted, game over.

ゲームオーバー...

2016年3月19日土曜日

[SICP][Lisp]デジタル回路のシミュレータ

SCIP 3.3.4 ディジタル回路のシミュレータ をCommon Lisp で書いみたので、貼っておこう。
インバータしか作っていないけど、こういうの作っていて楽しいね。

;; SICP Circuit simulator

;;----------------------------------------
;; queue

(defun make-queue ()
  (cons '() '()))

(defun front-ptr (queue)
  (car queue))

(defun rear-ptr (queue)
  (cdr queue))

(defun set-front-ptr! (queue item)
  (rplaca queue item))

(defun set-rear-ptr! (queue item)
  (rplacd queue item))

(defun empty-queue? (queue)
  (null (front-ptr queue)))

(defun front-queue (queue)
  (if (empty-queue? queue)
      (error "FRONT called with an empty queue ~a" queue)
      (car (front-ptr queue))))

(defun insert-queue! (queue item)
  (let ((new-pair (cons item '())))
    (cond
      ((empty-queue? queue)
       (set-front-ptr! queue new-pair)
       (set-rear-ptr! queue new-pair)
       queue)
      (t
       (rplacd (rear-ptr queue) new-pair)
       (set-rear-ptr! queue new-pair)
       queue))))

(defun delete-queue! (queue)
  (cond
    ((empty-queue? queue)
     (error "DELETE! called with an empty queue ~a" queue))
    (t
     (set-front-ptr! queue (cdr (front-ptr queue)))
     queue)))

;;----------------------------------------
;; time segment

(defun make-time-segment (time queue)
  (cons time queue))

(defun segment-time (segment)
  (car segment))

(defun segment-queue (segment)
  (cdr segment))

;;----------------------------------------
;; agenda

(defun make-agenda ()
  (list 0))

(defun current-time (agenda)
  (car agenda))

(defun set-current-time! (agenda time)
  (rplaca agenda time))

(defun segments (agenda)
  (cdr agenda))

(defun set-segments! (agenda segments)
  (rplacd agenda segments))

(defun first-segment (agenda)
  (car (segments agenda)))

(defun rest-segment (agenda)
  (cdr (segments agenda)))

(defun empty-agenda? (agenda)
  (null (segments agenda)))

(defun add-to-agenda! (time action agenda)
  (labels
      ((belongs-before? (segments)
  (or (null segments)
      (< time (segment-time (car segments)))))
       (make-new-time-segment (time action)
  (let ((queue (make-queue)))
    (insert-queue! queue action)
    (make-time-segment time queue)))
       (add-to-segments! (segments)
  (if (= time (segment-time (car segments)))
      (insert-queue! (segment-queue (car segments))
       action)
      (let ((rest (cdr segments)))
        (if (belongs-before? rest)
     (rplacd segments
      (cons (make-new-time-segment time action) rest))
     (add-to-segments! rest))))))
    (let ((segments (segments agenda)))
      (if (belongs-before? segments)
   (set-segments! agenda
    (cons (make-new-time-segment time action)
          segments))
   (add-to-segments! segments)))))

(defun remove-first-agenda-item! (agenda)
  (let ((q (segment-queue (first-segment agenda))))
    (delete-queue! q)
    (if (empty-queue? q)
 (set-segments! agenda (rest-segment agenda)))))

(defun first-agenda-item (agenda)
  (if (empty-agenda? agenda)
      (error "Agenda is empty -- FIRST-AGENDA-ITEM")
      (let ((segment (first-segment agenda)))
 (set-current-time! agenda (segment-time segment))
 (front-queue (segment-queue segment)))))

;;----------------------------------------
;; simulator

(defvar *agenda* nil)

(setf *agenda* (make-agenda))

(defun after-delay (delay action)
  (add-to-agenda! (+ delay (current-time *agenda*)) action *agenda*))

(defun propagate ()
  (if (empty-agenda? *agenda*)
      'done
      (let ((first-item (first-agenda-item *agenda*)))
 (funcall first-item)
 (remove-first-agenda-item! *agenda*)
 (propagate))))

(defun call-each (procs)
  (if (null procs)
      'done
      (progn
 (funcall (car procs))
 (call-each (cdr procs)))))

(defun make-wire ()
  (let ((signal-value 0)
 (action-procs '()))
    (labels ((set-signal! (value)
        (if (= value signal-value)
     'done
     (progn
       (setf signal-value value)
       (call-each action-procs))))
      (add-action! (proc)
        (setf action-procs (cons proc action-procs))
        (funcall proc))
      (dispatch (method)
        (case method
   ('get-signal signal-value)
   ('set-signal! #'set-signal!)
   ('add-action! #'add-action!)
   (t (error "Unknown operation ~a -- WIRE" method)))))
      #'dispatch)))

(defun get-signal (wire)
  (funcall wire 'get-signal))

(defun set-signal! (wire value)
  (funcall (funcall wire 'set-signal!) value))

(defun add-action! (wire proc)
  (funcall (funcall wire 'add-action!) proc))

(defun logical-not (value)
  (cond
    ((= value 0) 1)
    ((= value 1) 0)
    (t (error "Invalid signal ~a" value))))

(defvar *inverter-delay* 2)

(defun inverter (input output)
  (add-action! input
        #'(lambda ()
     (let ((new-value (logical-not (get-signal input))))
       (after-delay *inverter-delay*
      #'(lambda ()
          (set-signal! output new-value))))))
  'ok)

(defun probe (name wire)
  (add-action! wire
        #'(lambda ()
     (format t "~a ~a ~a~%"
      (current-time *agenda*)
      name
      (get-signal wire)))))

;;----------------------------------------
;; circuit

(defparameter w1 (make-wire))
(defparameter w2 (make-wire))

(inverter w1 w2)
(probe "w1" w1)
(probe "w2" w2)

動かしてみる。

0 w1 0
0 w2 0
CL-USER> (propagate)
2 w2 1
DONE
CL-USER> (set-signal! w1 1)
2 w1 1
DONE
CL-USER> (propagate)
4 w2 0
DONE
CL-USER>

もう少しいろいろ遊びたいところだけど、先に進もう。

2016年1月30日土曜日

[Emacs] org-modeで新しいメモを追加するElisp

ここ数年、Emacsでメモをとるのに、Change Logモードを使ってきた。
メモを書きたいときに、C-x 4 a (add-change-log-entry-other-window) で 、簡単に、新しいエントリをメモの最初に追加できるので便利だった。
最近、orgモードでメモをとり始めたのだけど、長年使ってきた 新エントリの追加ができず不便だったので、作ってみた。

(global-set-key (kbd "C-x 5 a") 'insert-memo-new-headline)

(defvar *memo-file* "~/org/memo.org")

(defun insert-memo-new-headline ()
  (interactive)
  (let ((memo-buffer (find-file-noselect *memo-file*)))
    (unless (eq (current-buffer) memo-buffer)
      (when (one-window-p t)
 (split-window))
      (other-window 1)
      (switch-to-buffer memo-buffer)))
  (goto-char 0)
  (insert "\n\n")
  (goto-char 0)
  (insert "* ")
  (org-insert-time-stamp nil)
  (insert " "))

C-x 5 a で *memo-file*で指定したファイルの最初に移動し、 図のような形式で、新しいメモを追加できるようになる。



久し振りにElispのコードを書いたけど、いろいろ忘れてしまっていた...
こういうときは、るびきちさんのEmacs Lispテクニックバイブルがとても便利。

2015年11月1日日曜日

[R][FX] Debian Jessie で Rのパッケージをインストールできないときの対処法

MetaTraderで自動売買しようと思い、日々、EA(Expert Advisor)を書いていたら、すっかりブログの更新が滞ってしまった。
為替で有効な手法を見つけるのは、なかなか難しい。

EAでいちいちシミュレーションするのは面倒だし、為替データの解析に、Rを使えると便利そうだなと思い、みんなのRを読みながら、例を入力しつつ、Rをいじりはじめた。

この書籍ではR Studioを推奨していたので、 Debian Jessie 標準のR Studioを使っている。

書籍に書いてあった株価データを扱うためのパッケージquantmodをインストールしようとしたら、 エラーになった。

> install.packages("quantmod")
Installing package into ‘/home/satoshi/R/x86_64-pc-linux-gnu-library/3.1’
(as ‘lib’ is unspecified)
Warning in install.packages :
  package ‘quantmod’ is not available (for R version 3.1.1)

CRANにはquantmodはあるようなのだけど、インストールできない。
StackOverflowを見て解決。

 インストールしたままでは、CRANのパッケージが全然見えていないようだ。

> setRepositories()
 --- このセッションで使うリポジトリーを選んでください --- 

1: + CRAN
2:   BioC software
3:   BioC annotation
4:   BioC experiment
5:   BioC extra
6:   CRAN (extras)
7:   Omegahat
8:   R-Forge
9:   rforge.net

 スペースで区切られた 1 つ以上の数を入力するか、キャンセルするには空白行を入力してください 
1: 1 ◆CRANが選択されているが、再度選択してみた。
> ap <- available.packages=""> View(ap) ◆パッケージを表示する。
パッケージが全く表示されない。 以下のコマンドでミラーを選択できる。
> chooseCRANmirror()
CRAN mirror 

  1: 0-Cloud [https]                 2: 0-Cloud                     
  3: Algeria                         4: Argentina (La Plata)        
--略--
 55: Italy (Palermo)                56: Japan (Tokyo)               
 57: Japan (Yamagata)               58: Korea (Seoul 1)             
--略--

Selection: 56 ◆日本のミラーを選択
> ap <- available.packages()
> View(ap) ◆パッケージを表示できた。
> 
> install.packages("quantmod") ◆quantmodをインストール
--略--
* installing *source* package ‘zoo’ ...
--略--
* installing *source* package ‘xts’ ...
--略--
* installing *source* package ‘TTR’ ...
--略--
* installing *source* package ‘quantmod’ ...
--略--
* DONE (quantmod)

The downloaded source packages are in
 ‘/tmp/Rtmp6M7HGP/downloaded_packages’
>
依存パッケージも一緒にインストールできた。 xtsパッケージは時系列データtsを拡張したクラスxtsを提供し、 不規則な間隔を扱うことができるので、 為替や株価データを扱うには最適のようだ。

参考

みんなのR
いくつかRの本を買ってみたが、この本が一番分かりやすかった。
グラフ描画ggplot2の説明も分かりやすいし、実例も多数あるのでおすすめ。

2015年6月23日火曜日

[コンピュータ開発][コンピュータシステムの理論と実践] OCamlでHack assemblerを実装(3)

Nand2tetris Software Suite に含まれている昔懐しピンポンゲーム(Pong.asm)を作成したアセンブラでアセンブルしてみた。

Pong.asmはおおよそ28,000ステップあるが、問題無くアセンブルでき、CPU Emulatorで動作した。


次は、Virtual Machineを実装する予定。楽しいねー。

2015年6月21日日曜日

[コンピュータ開発][コンピュータシステムの理論と実践] OCamlでHack assemblerを実装(2)

前回からずいぶん間隔が空いたが、Hackアセンブラを拡張し、変数とジャンプ先にシンボルを利用できるようにした。

コードはこちら。
https://github.com/takeisa/ocaml-hack-assembler

サンプルコード Test.asm
1から100まで加算し、シンボルiで示すアドレスに計算結果を格納するプログラム。
// sum from 1 to 100
        @i
        M=1     // i=1
        @sum
        M=0     // sum=0
        
(LOOP) // comment(space)
        @i
        D=M     // D=i
        @100
        D=D-A   // D=i-100
        @END
        D;JGT   // if (i-100) > 0 then jump END
        @i
        D=M     // D=i
        @sum
        M=D+M   // sum=sum+i
        @i
        M=M+1   // i=i+1
        @LOOP
        0;JMP
(END)
        @END
        0;JMP

シンボルテーブルを出力するようにしたので、実行すると以下のようになる。
satoshi@jessie: ./assembler Test.asm
=== Symbol table ===
        SP 0000
       LCL 0001
       ARG 0002
      THIS 0003
      THAT 0004
       R00 0000
       R01 0001
       R02 0002
       R03 0003
       R04 0004
       R05 0005
       R06 0006
       R07 0007
       R08 0008
       R09 0009
       R10 000a
       R11 000b
       R12 000c
       R13 000d
       R14 000e
       R15 000f
    SCREEN 4000
       KBD 6000
         i 0010
       sum 0011
      LOOP 0004
       END 0013

2015年5月17日日曜日

[Docker][Debian]JessieにDockerをインストール

Dockerのインストールの解説には、Debian Jessieへインストールする手順が書かれているが、2015/5/17現在、backportsには存在せず、apt-getでインストールするには、sidからインストールしないとできないようだ。

sidは使いたくなかったので、いろいろ調べてみると、
http://linuxconfig.org/package-docker-io-has-no-installation-candidate-debian-jessie
の手順が一番簡単そうだった。

インストール手順

curlが必要なので、あらかじめ入れておく。
# apt-get install curl

Dockerをインストールする。
# curl -sSL https://get.docker.com/ | sh

試す。
# docker run -i -t ubuntu /bin/bash

root以外のユーザでdockerコマンドを使いたい場合は、dockerグループを追加する。
$ sudo usermod -aG docker your-user

注意点

Debianの場合、
docker - KDE3/GNOME2 docklet アプリケーション用システムトレイ
というパッケージがあり、これをインストールすると、dockerコマンドが衝突してしまうかもしれない。
Dockerを動かすVMはX Windowを使わないので、気にしないことにした。

その他

最近、Dockerを使い始めたのだけど、コンテナの起動は早く、いろいろなコンテナを手軽に起動してサービスを動かすことができるのは、非常に便利で、使っていて面白い。