Project Euler 2(2)

http://projecteuler.net/index.php?section=problems&id=2


フィボナッチ数列を次のように定義すると、

let fib = seq {
   let mutable a = 1
   let mutable b = 1
   while true do
   yield b
   let tmp = b
   b <- a + b
   a <- tmp
}

mutableは使えないと怒られてしまいました。ここは参照を使うべきところのようです。平方を出すコードなら、

let sq = seq {
   let i = ref 0
   while true do
   yield !i * !i
   i := !i + 1
}

printfn "%A" (Seq.toList (Seq.take 5 sq))

意味はコードを見ればわかるでしょう。
結局フィボナッチ数列は、

let fib = seq {
   let a = ref 1
   let b = ref 1
   while true do
      yield !b
      let tmp = !b
      b := !a + !b
      a := tmp
}

let N = 4000000
printfn "%d" (Seq.sum (Seq.filter (fun n -> n % 2 = 0) 
                        (Seq.takeWhile (fun n -> n <= N) fib)))

長たらしくなってしまいました。(インデントはこれでいいんだ。昨日のはなんだったんだ?)
実はunfoldという関数を使えば簡単に書けます。unfoldは、

Seq.unfold f 状態0

とすると、

状態0 -> f(状態0) = (値1, 状態1) -> f(状態1) = (値2, 状態2) -> …

と計算していき、

seq [ 値1; 値2; … ]

が返るという関数のようです(実際には遅延評価と思われる)。これは便利ですね。

let fib = Seq.unfold (fun t -> Some(snd t, (snd t, fst t + snd t))) (1, 1)

let N = 4000000
printfn "%d" (Seq.sum (Seq.filter (fun n -> n % 2 = 0) 
                (Seq.takeWhile (fun n -> n <= N) fib)))

takeWhileもふつうに使えます。
SomeってついているのはMayBeモナドっぽく使うためなんですかね。