読者です 読者をやめる 読者になる 読者になる

グローバル引きこもりブログ

「Common Lispと関数型プログラミングの基礎」というプログラミングの本を書いてます。他に「引きこもりが教える! 自由に生きるための英語学習法」という英語学習の本も書いています。メール acc4297gあっとgmail.com

意外に新しい事というのはあるもんだと思った

数学的に「関数fが1からnまでの数を足す関数である」という事を表すにはどうすればいいだろうか?これは次のように再帰的な定義をすればよい。

f(n) = n + f(n-1)
..
f(5) = 5 + f(4)
f(4) = 4 + f(3)
f(3) = 3 + f(2)
f(2) = 2 + f(1)
f(1) = 1

これは直接コンピューターのプログラムにすることができて、例えばLispだと普通は次のように定義される。

 
CL-USER> (defun f (n)
	   (if (= n 1)
	       1
	       (+ n (f (- n 1)))))
F
CL-USER> (f 100)
5050

これは再帰関数を説明する際によく出てくるもので、僕の関数型プログラミングの本でもこの関数についてはかなり詳しく解説した。しかし本を改定するときに気づいたんだけど、この関数は次のように定義したほうが上の図のような説明の後では分かりやすい。

    
CL-USER> (defun g (n)
	   (if (<= 2 n) ; あるいはif (/= 1 n)
	       (+ n (f (- n 1)))
	       1))
G
CL-USER> (g 100)
5050

これは意味的には(定義域の範囲内で)全く同じで、プログラムの実行速度も変わらないが、しかし上の図と一対一で対応しているプログラムは後の方のプログラムだろう。

そう考えて新しい版はプログラムを後の方に変えたのだが、僕は始めの版を書いている時にはこの関数は最初のように書くのが唯一の書き方だと思っていた。もっと正確にいうならば

    
(defun f2 (n)
  (cond ((= n 1) 1)
        ((= n 0) 0)
        (t (+ n n -1 (f2 (- n 2))))))

という関数も取り上げてはいるのだが、2番目の書き方には気づかなかった。気づかなかったからこれ以外の書き方はないと確信していたのだが、しかしそれ以外にも書き方が存在していたわけである。こういう単純な話でも気づいていない点があるのか、と驚いた。

本の他の部分でも数か所、こういう気づかない点があった。

前の版を出版したときには、もう本の範囲で扱えるネタは出し尽くしたと思っていたけど、実際には他にも面白い小ネタがいくつかあったので、新しい版ではそれらのネタも追加してある。

それから前の版ではconsultantという、日付を入力したら1年の何パーセントが過ぎたかを表示するやや無様な書き方をしているプログラムがあったのだが、これは完全に書き換えた。このプログラムは以前からあまり気にいらなかったものだが、プログラムをうまくバラバラに分解する方法を思いついたので新しい版では書き換えてある。

あとは多分、特に変える必要がある所はないと思うけれども、確信というのは間違っている事もあるのだ、という事を実感した。