第2夜:0はえらい

数の悪魔*1を題材にschemeプログラミング第二回です。

5の1乗、5の2乗、5の3乗。これをな、5をホップさせる、と言うんじゃ。わかったかな。おなじことを10でやると、もっと簡単だ。計算機なんかなくったって、すらすらできる。10を1回ホップさせれば、そのまま10だ - 数の悪魔

(define (hop b n)
	(define (hop-iter v n)
		(if (< n 1)
			v
			(hop-iter (* b v) (- n 1))))

	(hop-iter 1 n)
)

(define (main args)
	(print (hop 10 0))
	(print (hop 10 1))
	(print (hop 10 5))
	(print (hop 10 10))
0)

結果はこちら。

1
10
100000
10000000000

直球だとこんな感じになりました。ここで、ちょっとトラウマになりかけたSICP*2を思い出して。

(define (hop b n)
	(define (hop-iter a b n)
		(if (< n 1)
			a
			(if (even? n)
				(hop-iter a (* b b) (/ n 2))
				(hop-iter (* b a) b (- n 1)))))

	(hop-iter 1 b n)
)

(define (main args)
	(print (hop 10 0))
	(print (hop 10 1))
	(print (hop 10 5))
	(print (hop 10 10))
0)

おまけでローマ数字変換。効率のいい書き方がありそう。nとキャラクタをconsセルでまとめて返して、lambda式で受ける部分は完全に自己流。もっといい方法あるかも。

(define (roman-numeral n)
	(define (roman-num n)
		(cond
			((>= n 1000) (cons (- n 1000) "M"))
			((>= n  900) (cons (+ n  100) "C"))
			((>= n  500) (cons (- n  500) "D"))
			((>= n  400) (cons (+ n  100) "C"))
			((>= n  100) (cons (- n  100) "C"))
			((>= n   90) (cons (+ n   10) "X"))
			((>= n   50) (cons (- n   50) "L"))
			((>= n   40) (cons (+ n   10) "X"))
			((>= n   10) (cons (- n   10) "X"))
			((>= n    9) (cons (+ n    1) "I"))
			((>= n    5) (cons (- n    5) "V"))
			((>= n    4) (cons (+ n    1) "I"))
			((>= n    1) (cons (- n    1) "I"))))

	(define (roman-iter n l)
		(if (< n 1)
			(reverse l)
			((lambda (x)
				(roman-iter (car x) (cons (cdr x) l))) (roman-num n))))

	(if (> n 3999)
		"Please less than 4000."
		(roman-iter n '()))
)

(define (main args)
	(print (roman-numeral 1986))
0)

実行結果はこちら。

(M C M L X X X V I)

SICPは26ページで挫折中。いつか続きをやりたいです。

*1:Hans Magnus Enzensberger,1997,Der Zahlenteufel,エンツェンスベルガー 丘沢静也(訳),2000,数の悪魔,晶文社

*2:Harold Abelson and Gerald Jay Sussman with Julie Sussman,1996,Structure and Interpretation of Computer Programs -- 2nd ed.,ジェラルド・ジェイ・サスマン ハロルド・エイブルソン ジュリー・サスマン 和田英一(訳),2000,計算機プログラムの構造と解釈 第二版,ピアソン・エデュケーション