本当のプログラミング初心者がSICPを読んではいけない三つの理由

  • 酔って適当に考えたことを書き留めておく
  • 半分以上与太だけど、ツッコミ歓迎

SICPは「計算機プログラムの構造と解釈」というコンピュータサイエンスの教科書です。サンプルはすべてSchemeというLispの方言で書かれています。以下、「本当にプログラミング未経験でSICPから入門しようとする人」が陥るかもしれない三つの罠を挙げてみます。

1. 破壊的代入に対する嫌悪感を植えつけられる

SICPには「大リーグボール養成ギプス」のような側面があります。ストイックな制限を与えることで思考の流れを誘導する感じ。第1章ではリストが使えず(LISP=LISt Processingなのに!)、第2章まではset!が使えないため破壊的代入ができません。つまり状態が扱えない(実際には2章の最後のほうではテーブルが最初から用意されていることにしてオブジェクト指向っぽいことをしてますが)。第3章でようやく状態を扱えるようになりますが、「状態ってめんどくさいよねー」という例がさんざん出てきた挙句、最後に「遅延ストリームがあれば代入はなくてもよくね?」という展開。プログラミング経験がある人はだまされないと思いますが、真っ白な状態から入った人はたぶん破壊的代入が生理的に嫌いになります。というか私はなりました。おそらくSICPの目的の一つは関数型スタイルへの洗脳です。

ただ、オブジェクト指向プログラミングをはじめ、普通のプログラミングでは状態を普通に使います。当然。破壊的代入がイヤだと駄々をこねるのは現実的ではありません。遅延評価やモナドを持つHaskelにそのままなだれ込めば幸せな人生を送れるのかもしれませんが、ALGOL系の言語を全く知らずにHaskelのコードをバリバリ書いている人には、私はいまだにお目にかかったことはありません。

ということで、何事にも限度というものはあるのです。

2. ループが嫌いになる

Schemeで繰り返し構造を書く方法は再帰しかありません(というのは少しウソですが)。必然的にSICPのサンプルは再帰だらけです。リスト操作ではmapのような高階関数も多用しますが、ちょっと凝ったことをしようとすると再帰再帰です。再帰は慣れるまでにちょっとした壁はありますが、慣れると書くのが楽しくなります。もしかしたらRubyの楽しさに似ているのかもしれません(私はRubyistではないのでよくわかりませんが)。再帰を利用すると、ただのループでは書くのが面倒な、込み入った場合分けなどを簡潔に記述できます。

再帰はスタックを消費するので普通は避けたほうがよいとされていますが、Schemeでは末尾再帰は最適化される(リソースを消費しない)と決まっています。なので、Schemeプログラマ再帰を多用すると同時に、自然に末尾再帰で書くようになります。とはいえ、なんてことない繰り返しまでループではなく再帰で書きたがるのは問題です。こうした姿勢はScheme以外の「標準でループが用意されている言語」ではリソースの無駄遣い、つまり明らかに有害です。

繰り返しますが、何事にも限度というものはあるのです。

3. 何でも自分で実装してしまう

Schemeの哲学は「言語仕様は小さいほうがいい」というミニマリズムです。SICPの演習問題を解いていると、carとcdrとconsとcondくらいあれば何でも書けるような気がしてきます(理屈の上ではあとdefineとeq?とatom?とquote(')があれば十分みたいです)。たまに勢いあまってmemqのような標準関数に相当する処理まで自分で書いてしまうこともあります(これはさすがにやりすぎ)。SchemeにはSRFIという拡張仕様もあるのですが、SICPの問題はSchemeの標準機能で解くことが前提になっているので、SRFIを探してしまうと自分の勉強にならないということで、「ないものは探すのではなく書く」という習慣が付いてしまいます。

Javaのように、どんなときにどんなライブラリを使えばいいかを知っていることが開発効率に直結する開発環境では、このような姿勢は致命的です。というか「車輪の再発明をするな」とか「怠惰はプログラマの美徳」とか、とかく自前主義は評判が悪いですね。

しつこいようですが、何事にも限度というものはあるのです。


ただ、プログラミングの経験があって、しかもこれまで関数型プログラミングを意識したことのない人は、上の三つの要素を少しは持っていたほうがよいかもしれません。ということで、やはりSICPはよい入門書だと思います。