Project Euler 8

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


文字列は文字の配列らしいです。個々の文字にアクセスするには、

let s = "123"
printfn "%c" s.[0]  // 1

これを数にするには、intに変換すればコードになるのでそれを用いればよいでしょう。

printfn "%d" (int s.[0] - int '0')  // 1

前問と同様にこういう場合にListとSequenceとどちらを用いるべきかがわかりません。Listにはtakeがないし、SequenceにはTailが見当たりません。仕方がないのでListを使いtakeは自作します。

let rec take n list = if n = 0 then [] else if list = [] then []
                        else list.Head :: (take (n - 1) list.Tail)

次々に5つずつ取り出すにはunfoldを使えばよいでしょう。unfoldはNoneが返ってくるとそこで終わります。簡単な例で、

let range m n = Seq.unfold (fun s -> if s >= n then None
                                       else Some((s, s + 1))) m
printfn "%A" (range 1 3)   // seq [1; 2]

結局、

let s =
   "73167176531330624919225119674426574742355349194934" 
 + "96983520312774506326239578318016984801869478851843"
   ...
 + "71636269561882670428252483600823257530420752963450"

let rec take n list = if n = 0 then [] else if list = [] then []
                        else list.Head :: (take (n - 1) list.Tail)
let digits = [ for c in s -> int c - int '0' ]
let N = 5
let a = (Seq.unfold (fun s -> if s = [] then None
                              else Some((take N s, s.Tail))) digits)
let product list = Seq.fold (fun x y -> x * y) 1 list

printfn "%d" (Seq.max (Seq.map product a))

Listを使って書きましたが、Sequenceを使うとどうなるでしょう。よく見ると、Tailの代わりにskipという関数を使えばよいようです。

let digits = seq [ for c in s -> int c - int '0' ]
let N = 5
let a = (Seq.unfold (fun s -> if Seq.length s < N then None
                              else Some((Seq.take N s, Seq.skip 1 s))) digits)
let product list = Seq.fold (fun x y -> x * y) 1 list

printfn "%d" (Seq.max (Seq.map product a))

とても遅いです。skipが遅いのでしょうか。