円周率を求めてみる

30分プログラム、その743。今日3/14は円周率の日らしいです。というわけで、円周率を求めてみました。
計算式は円周率の計算 - みずぴー日記で使ったやつと同じです。
直接計算するのは芸がないので、無限ストリームを定義して、その上でだんだんと精度が上っていくπのストリームを作りました。

使い方

- Stream.nth pi 100;
val it = 3.09616152646 : real
- Stream.nth pi 1000;
val it = 3.13659268484 : real
- Stream.nth pi 1000;
val it = 3.14109265362 : real

ソースコード

structure Stream =
struct
  type 'a thunk = unit -> 'a;
  datatype 'a stream = Cons of 'a * ('a stream) thunk;

  fun force f = f ();
  fun cons x y = Cons (x,y);
  fun hd (Cons(x,_)) = x;
  fun tl (Cons(_,xs)) = force xs;

  fun nth xs 0 = hd xs
    | nth xs n = nth (tl xs) (n-1);

  fun scanl f init (Cons (x,xs)) =
      cons init (fn () => scanl f (f (x,init)) (force xs));

  fun map f (Cons (x,xs)) =
      cons (f x) (fn () => (map f (force xs)));
end;

fun term i =
    let
	val a = (Real.fromInt i) * 4.0 + 1.0
	val b = a + 2.0
    in
	1.0 / (a * b)
    end;

fun pi_8 n =
    Stream.cons (term n) (fn () => pi_8 (n+1));

val pi =
    Stream.map (fn x => 8.0 * x) (Stream.scanl (fn (x,y) => x + y) 0.0 (pi_8 0));