すげぇな。
で、なんともなしに円周率を求めるアルゴリズムを見てたわけですが。
あれ?
Wikipediaでこんなん見つけた。
これって・・・・・・ハナクソじゃね?
フツーのプログラミングだったら反復で書く為にあれこれ試行錯誤せなアカンが、Schemeには末尾再帰最適化がある。
それが意味する事は何も考えなくても実装が可能だと言うことだ。
式をそのままSchemeに置き換えていけばいい。
(define (a n)
(let loop ((i 0) (init 1))
(if (= i n)
init
(loop (+ i 1) (/ (+ init (b i)) 2)))))
(define (b n)
(let loop ((i 0) (init (/ 1 (sqrt 2))))
(if (= i n)
init
(loop (+ i 1) (sqrt (* (a i) init))))))
(define (t n)
(let loop ((i 0) (init 1/4))
(if (= i n)
init
(loop (+ i 1)
(- init (* (p i)
(expt (- (a i) (a (+ i 1))) 2)))))))
(define (p n)
(let loop ((i 0) (init 1))
(if (= i n)
init
(loop (+ i 1) (* 2 init)))))(define (π n)
(/ (expt (+ (a n) (b n)) 2) (* 4 (t n))))
ほらな、人間コンパイラ達がアレコレ考えてる間に我々関数型言語派は何も考えずに数式をそのままプログラムに置き換えていけるのだ。
万歳!
ちなみに、遅延評価を使ってこんな方法も可能だ。
例によって遅延評価ライブラリのSRFI-41を用いる。
(define a
(stream-cons 1
(stream-map
(lambda (x y)
(/ (+ x y) 2)) a b)))
(define b
(stream-cons (/ 1 (sqrt 2))
(stream-map
(lambda (x y)
(sqrt (* x y))) a b)))
(define t
(stream-cons 1/4
(stream-map
(lambda (x y z w)
(- x (* y (expt (- z w) 2))))
t p a (stream-cdr a))))
(define p
(stream-cons 1
(stream-map
(lambda (x)
(* 2 x)) p)))
(define π
(stream-map (lambda (x y z)
(/ (expt (+ x y) 2) (* 4 z)))
a b t))
無限に長いπ列、先に進めば進む程正確な円周率に近づいていく・・・って言いたいトコだけど、今んトコ桁数の制限があってつまらんな。
いずれにせよ、数学的「アルゴリズム」はLisp等の関数型言語だと簡単に書けるぞ、と言う実例。