SICPを読み始めた

SICPを何年か前に読んでいたのですが、途中まで読んでそのまま積ん読になってました。そのことをふと思い出したのでまた読み始めております。読みながらコード書く時の環境についての日記です。

SICPとは計算機プログラムの構造と解釈という本の略称。

ja.wikipedia.org

翻訳したPDFを公開してくれている方がおり、とても感謝です。

github.com

どんな感じで読もうか

REPLを使う( Guile + rlwrap )

読みながらコード写経したくなったり問題を解きたいときがあります。そういうときはGuileのREPLを使ってます。PCはmacchromebookか日によって。

www.gnu.org

guile で起動して、 ,h でヘルプ、 ,q で終了。

$ guile
scheme@(guile-user)> (+ 1 2)
$1 = 3
scheme@(guile-user)> ,h
Help Commands [abbrev]:

 ,help [all | GROUP | [-c] COMMAND]
                              [,h] - Show help.

...

scheme@(guile-user)> ,q

このままだと、 Ctrl-pCtrl-a が効かず辛いのでrlwapを使います。rlwrapは対話モードで動くようなコマンドにreadlineの機能を後付けして、コマンド履歴を Ctrl-p で遡れるようにしたりEmacsキーバインドを使えるようにしてくれます。

github.com

guile単体で起動して Ctrl-p すると以下のようになる。

$ guile
scheme@(guile-user)> (+ 1 2)
$1 = 3
scheme@(guile-user)> ; Ctrl-pを入力すると...
scheme@(guile-user)> ^[[A  ; Ctrl-p が使えない!

rlwrapといっしょに使う。rlwrap便利!

$ rlwrap guile
scheme@(guile-user)> (+ 1 2)
$1 = 3
scheme@(guile-user)> ; Ctrl-pを入力すると...
scheme@(guile-user)> (+ 1 2)  ; 履歴が使える!

ファイルに書いて読み込む

関数の行数が増えてくるとREPLで記述するのが辛いし間違えて消しちゃったら悲しいので、VS Codeで書いてファイルに保存してからREPLで読み込んで実行してます。

VS Codescheme拡張機能でコードハイライトとインデントづけを楽に。

marketplace.visualstudio.com

rlwrapの -c オプションを使うとファイル名の補完が効くので便利。

;; fib.scm

(define (fib x)
  (cond ((= x 0) 0)
        ((= x 1) 1)
        (else (+ (fib (- x 1)) (fib (- x 2)))))
)
$ rlwrap -c guile
scheme@(guile-user)> (load "fib.scm") ; tabでファイル名の補完が効く!
scheme@(guile-user)> (fib 5)
$1 = 5

,trace

,trace使うとこんな風に表示される。

scheme@(guile-user)> ,trace (fib 5)
trace: (fib 5)
trace: |  (fib 3)
trace: |  |  (fib 1)
trace: |  |  1
trace: |  |  (fib 2)
trace: |  |  |  (fib 0)
trace: |  |  |  0
trace: |  |  |  (fib 1)
trace: |  |  |  1
trace: |  |  1
trace: |  2
trace: |  (fib 4)
trace: |  |  (fib 2)
trace: |  |  |  (fib 0)
trace: |  |  |  0
trace: |  |  |  (fib 1)
trace: |  |  |  1
trace: |  |  1
trace: |  |  (fib 3)
trace: |  |  |  (fib 1)
trace: |  |  |  1
trace: |  |  |  (fib 2)
trace: |  |  |  |  (fib 0)
trace: |  |  |  |  0
trace: |  |  |  |  (fib 1)
trace: |  |  |  |  1
trace: |  |  |  1
trace: |  |  2
trace: |  3
trace: 5

末尾再帰の形に書き換えると、反復処理になってることが分かる。ちなみに fib 内部で fib-iter を記述すると ,trace の出力にでてこない、残念。出力させる方法あるのかな。

;; fib.scm

(define (fib x)
  (fib-iter x 1 0)
)

(define (fib-iter x a b)
  (cond ((= x 0) b)
        (else (fib-iter (- x 1) (+ a b) a)))
)
scheme@(guile-user)> ,trace (fib 5)
trace: |  (fib 5)
trace: |  (fib-iter 5 1 0)
trace: |  (fib-iter 4 1 1)
trace: |  (fib-iter 3 2 1)
trace: |  (fib-iter 2 3 2)
trace: |  (fib-iter 1 5 3)
trace: |  (fib-iter 0 8 5)
trace: |  5

おしまい

こんな感じで読み進めて行こう。問題解いたらとりあえずgistに追加していこっかなぁ。

_SICPの問題等を解いていく_1-3章.md · GitHub