Hatena::ブログ(Diary)

ヤドカリデンキ商会(第一倉庫)

2008-02-26

本当のプログラミング初心者が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はよい入門書だと思います。

naoya_tnaoya_t 2008/03/04 01:58 釣られてやって来ましたww

BASIC→Z80アセンブラ→C言語、な世代ですが、SICP的なものにもうちょっと早い時期に(できれば高校生ぐらいに)出会っていたらとは思います。

最初の教科書としては、職業プログラマになるまでに数年の猶予がある学生にはまあいいから読めと言いたいですが、即戦力が期待されている人には確かにどうかと思います。

個人的には、CとかC++で何でも書ける!気でいたのを見事に崩してくれた良い薬でした。

yad-ELyad-EL 2008/03/04 13:09 たぶん、SICPで入門→次にCを学んで目からウロコ、というのが一番幸せなパターンなんだろうと思います。周りではそんな人は見たことないけど、アメリカのコンピュータサイエンスの授業はその順番みたいですね

talootaloo 2008/03/30 11:30 初めまして。

破壊的代入についてなんですが、確かにSICPを読んでからというもの、以前より一層嫌いになりました。
より一層と言うからには以前から破壊的代入が嫌いだったわけなんですが。

なんといっても一番の理由は、並列処理における破壊的代入の扱いに関する面倒さです。同期するとか、ここは並列にしていい
とか、面倒臭すぎます。

そこでSICPには、どのようにして破壊的代入なしに、関数型スタイルで状態を表現するのか、ということに
期待していました。それで出てきたのがストリームだったわけなんですけど、正直「うーん」という感じですね。

やはり行き着く先は Haskell のモナドなんでしょうかねぇ。

rerorero 2008/06/24 00:58 「破壊的代入」って言葉に違和感があるのですが,「代入」と言った時点で破壊的操作を表しませんか?「代入」を破壊的ではない意味で用いたいときは「束縛」など他の表現をするような気がするのですが….

rerorero 2008/06/24 01:09 うーん….「破壊的代入」でぐぐると 1,500 件近く引っかかりますね.一般的なのかな?

yad-ELyad-EL 2009/12/18 12:33 「代入」と「束縛」を使い分ければいいような気がしてきました。