Project Euler 4

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


全ての組合せに対し、回文数であるかどうか判定し、回文数の中で最大のものを求めます。
回文数の判定は、各桁の数字ごとのリストにし、それを逆にし、数に戻して元の数と同じか調べます。
まず、数字のリストにするのは再帰で簡単に書けます。

let rec digits n = if n = 0 then [] else (digits (n / 10)) @ [n % 10]

@はリストの結合です。
逆順にする関数はList.revです。
再び数に戻すのは素直にfoldで書きます。

let to_number s = List.fold (fun x y -> x * 10 + y) 0 s

F#にも内包表記があります。

let a = [ for i in 1..5 -> i * i ]
printfn "%A" a
[1; 4; 9; 16; 25]

2次元の例は見当たらなかったのですが、適当に試行錯誤したらできました。

let a = [ for x in 1..3 do for y in x..3 -> (x, y) ]
printfn "%A" a
[(1, 1); (1, 2); (1, 3); (2, 2); (2, 3); (3, 3)]

これでこの問題は解けます。

let rec digits n = if n = 0 then [] else (digits (n / 10)) @ [n % 10]
let to_number s = List.fold (fun x y -> x * 10 + y) 0 s
let is_palindromic n = n = to_number (List.rev (digits n))

printfn "%d" (List.max (List.filter is_palindromic
               [ for n in 100..999 do for m in n..999 -> n * m ]))

タスクマネージャ見たところ、これって勝手に並列化しているみたいですね。
でも、数を大きくするとメモリをやたらと食います、遅延評価しないので。Sequence同じです。

printfn "%d" (Seq.max (Seq.filter is_palindromic
                (seq [ for n in 100..999 do for m in n..999 -> n * m ])))

内包表記は捨ててこう書くと遅延評価するようですね(数の範囲を変えている)。

let s = seq {
    for n in 300..2999 do
        for m in n..2999 do
            yield n * m
}

printfn "%d" (Seq.max (Seq.filter is_palindromic s))

Haskellの倍近く実行時間がかかります。