SICPがマイブーム。今月から渋谷某社の週次勉強会に参加させて頂き、勉強していくことにした。
全文がMITの本家サイトで読めるので本を買わなくても勉強は出来るけどアサマシ貼っとく。
SICP(Structure and Interpretation of Computer Programs)はMITで利用されていた計算機科学の古典的教科書で、言語としてLISPの方言であるSchemeを用いている。
今の僕の理解でSICPの価値を説明する意義はあまりないと思うので、SICPを読んできた先人達のリンクを紹介するに留める。
- SICP関数型言語の勉強に「計算機プログラムの構造と解釈」を読もう - Higepon’s blog - Mona OS and Mosh
- SICP(計算機プログラムの構造と解釈)を読み終えて : Serendip - Webデザイン・プログラミング
- 「計算機プログラムの構造と解釈(SICP)」を読み終えて by なつたん: なつたん
とりあえず実行環境
Mac OS X 10.7.2の上で
% brew install gauche
してScheme処理系であるGauche(合ってる?)をインストール。
% gosh
gosh> (define (square x) (* x x))
square
gosh> (exit)
goshを出るときはexitもカッコでくくる必要があるので注意。
あると便利な手続きをかき集める
基本的にはgoshを使えばぽちぽち例を入れていけるんだけど、勉強を進める上で
- 実行速度を測定する手続き
- 再帰で呼び出される内容を1回1回出力する
のふたつが使えると便利だと思う。
実行速度を測定する手続き
再帰的手続きで書くとこれだけの時間がかかるけど、反復的に書くとほらこんなに速い!という文脈が出てきて、SICP本文を読むと実行速度を比較するためにruntimeを使え、となっているが
gosh> (runtime)
*** ERROR: unbound variable: runtime
Stack Trace:
_______________________________________
使えない。どうも自分で定義してやる必要があるらしい。実装はググるといろいろあるようだけど、僕は以下のものを使った。
(define (runtime)
(use srfi-11)
(let-values (((a b) (sys-gettimeofday)))
(+ (* a 1000000) b)))
再帰で呼び出される内容を1回1回出力する
再帰と反復の違いがとりあえず最初のヤマだと思う。僕も、もうちょっと手を動かし続けないと腑に落ちていない感じがする。
内部処理を1回1回出力してくれるtraceという便利な手続きがあるのだが、Gaucheには標準で入っていない。入れ方はdankogai氏が404 Blog Not Found:scheme - traceとslib解説してくれている。入りさえすればあとは
(use slib)
(require 'trace)
で使えるようになる。
よく使う手続きを定義したファイルを使い回す
僕が使い回しているmy_defs.scmの中身は現時点で次の通り。
(use slib)
(require 'trace)
(define true #t)
(define false #f)
;; return micro seconds.
(define (runtime)
(use srfi-11)
(let-values (((a b) (sys-gettimeofday)))
(+ (* a 1000000) b)))
(define (average x y)
(/ (+ x y) 2))
(define (divides? a b)
(= (remainder b a) 0))
(define (square x) (* x x))
(define (cube x) (* x x x))
(define (power x n)
(if (= n 1)
x
(* x (power x (- n 1)))))
章が進むに従っていろいろ追加されていくと思うが、githubに上がっているものが最新のバージョンになると思う。
外部のファイルをロードする
Schemeで外部のファイルをロードする方法を探してみたところ、load-pathにカレントディレクトリを追加してloadすれば読み込める模様。 ファイル冒頭で次のようにして使っている。
(add-load-path ".")
(load "my_defs")
他にも「セクション1.2.2で定義した手続きを問題1.20で使いたい」というような要望が出てくる。 僕のように写経しながら進めるのであれば、セクション1.2.2のファイルを問題1.20の冒頭で読み込んでやればいい。
(add-load-path ".")
(load "sec1.2.2")
ただしprintとかしてるといちいちloadの度に出力されてしまうので、使い回す予定のファイルは書き方を気をつけなければならない。
VimShellで別ファイルのコードを選択して対話的に実行させる
Scheme使い(PHP使い=ぺちぱー、みたいな呼び名はないのだろうか)はEmacsを駆使しているイメージがあるが、Vim使いに生まれた人もSchemeをやりたい。そんなあなたにこちらのVimShell。VimShellの機能のひとつVimShellInteractiveを使って、編集中ファイルの任意の部分をirbやgoshなどの対話シェルに送り、実行させる。
インストール方法は vimshellが便利過ぎる件 - ぷろぐらま はっくす こちらの記事を参照のこと。

まずこうやってVimShellウィンドウを開いてやる。

右側にgoshインタプリタのウィンドウが開いた。

実行したい部分を選択して...

:VimShellSendStringを実行してやる(実際は押しやすいkey mappingを使うのがベター)。

すると、goshのウィンドウに選択部分の文字列が送られ,実行される。

次々に実行していった事後画面。なんか途中で順番がごちゃっとしてしまいましたが。
これができるようになると非常に捗る。オヌヌメ。
QuickRunで実行する
VimShellを使うと理想の実行環境が手に入るが、正直なところインストールのハードルが高い(と思う)。僕も2回くらい挫折してヒサビサに挑戦したところあっさり導入できた。
VimShellはまだ早い、でもファイルから直接実行したい。そんなあなたにはQuickRun.
インストール方法はVim-users.jp - Hack #7: 編集中ファイルを実行し、結果を表示したままにするを参照のこと。編集中のファイルで<Space>+rを押すだけで即座に実行結果を見ることができる。
QuickRunはVimShellと比較したとき
- コードを選択して実行できるものの、次の実行時にその結果を覚えていない
という作りのために対話的な実行には不向きであるが、十分以上に実用に足るし、むしろちょっとしたコードを書いてすぐ実行するだけであればベターな選択でもある。僕も日々RSpecでBDDするときやmarkdownでテキストを書いてQuickRun+openbrowserでHTMLを確認、などというケースで便利に使っている。
VimShellInteractive余談
ところでVimShellInteractiveにおいて、goshは快適に使えるのだが、いかんせんirbが想定通りに動いてくれない。いずれのインタプリタにせよ
- テキストを選択
- :VimShellSendStringを実行する(実際は使いやすいmappingを定義)
- VimShellInteractiveに選択部のStringが送られる
- 送られた文字列が実行される
という流れを想定しており、goshはこの通りに動く。ところがirbは(3)までは完了してテキストが流れ込むものの、(4)の実行が行われない。テキストを入力してエンターを押さずにいるような状態にみえる。再度VimShellInteractive画面に戻り、normal modeからinsert modeに入れば、思い出したかのようにStringが実行される。
insert modeのまま(ESCで抜けてnormal modeに戻ることなく)分割windowを移動する方法はないものか(本来あるべき解決ではないと思うけど
VimShellじゃなくConqueShellで同じような現象に困ってる人
いじょ
まあまだ僕が語れることは少ない。 直感だけど今の僕に必要な知識である気がしたので、ちょいと気合い入れて勉強してみますよ。たのしーし。数式見るのとか久しぶり。。。


