第15回オフラインリアルタイムどう書くの問題をF#で

[第15回オフラインリアルタイムどう書くの問題](http://qiita.com/Nabetani/items/cba03c96d1ea55f6e861)を解いてみました。

過去問ですが、解いてなかったので。

『プログラミングHaskell』に出てくるパーサコンビネータ風です。FParsecを使ってもいいのですが、大げさかなと思ったので必要そうなところだけつまみました。

(追記) `chars` 関数のところ、最初は `Word` オブジェクトにしていたのですが、それだと再帰の警告が出たので（`#nowarn "40"` で警告を消せますが……）、promise的な関数に変えておきました。

```fsharp
type column = int * int
type board = column list
type Parser<'a> = P of (board -> ('a * board) option)

let parse (P p) inp =  p inp

let eq c = P (function | []    -> None
                       | x::xs -> if x = c then Some (x,xs) else None)

let unit v =  P (fun inp -> Some (v,inp))

let (>>=) p f = P (fun inp -> match parse p inp with
                              | Some (v,out) -> parse (f v) out
                              | _            -> None)

let (<|>) p q = P (fun inp -> match parse p inp with
                              | None -> parse q inp
                              | x    -> x)

let SP =
    eq (0,0) >>= fun _ -> unit ' '

let JorS =
    eq (0,1) >>= fun _ ->
    eq (1,1) >>= fun _ ->
        (eq (1,0) >>= fun _ -> unit 'S')
        <|>
        unit 'J'

let TorZ =
    eq (1,0) >>= fun _ ->
    eq (1,1) >>= fun _ ->
        (eq (1,0) >>= fun _ -> unit 'T')
        <|>
        (eq (0,1) >>= fun _ -> unit 'Z')

let ThenLorU =
    eq (0,1) >>= fun _ ->
        (eq (1,1) >>= fun _ -> unit 'U')
        <|>
        unit 'L'

let ThenRorN =
    eq (1,0) >>= fun _ ->
        (eq (1,1) >>= fun _ -> unit 'N')
        <|>
        unit 'R'

let LorUorRorN =
    eq (1,1) >>= fun _ ->
        ThenLorU
        <|>
        ThenRorN

let Char = SP <|> LorUorRorN <|> JorS <|> TorZ

let rec chars _ =
    Char >>= fun x ->
        ((chars ()) >>= fun xs -> unit (x::xs))
        <|>
        unit [x]

let readBoard board =
    match (parse (chars ()) board) with
    | Some (cs,_) -> List.filter (fun x -> x <> ' ') cs
                     |> fun xs -> new string(List.toArray xs)
    | _           -> ""

let toBinary (s: string) = 
    let f = function
        | '0' -> [0;0;0;0] | '1' -> [0;0;0;1] | '2' -> [0;0;1;0] | '3' -> [0;0;1;1]
        | '4' -> [0;1;0;0] | '5' -> [0;1;0;1] | '6' -> [0;1;1;0] | '7' -> [0;1;1;1]
        | '8' -> [1;0;0;0] | '9' -> [1;0;0;1] | 'a' -> [1;0;1;0] | 'b' -> [1;0;1;1]
        | 'c' -> [1;1;0;0] | 'd' -> [1;1;0;1] | 'e' -> [1;1;1;0] | 'f' -> [1;1;1;1]
        | _   -> []
    s.ToCharArray()
    |> Array.toList
    |> List.collect f

let solve (s: string) =
    s.Split('/')
    |> Array.map toBinary
    |> function | [|xs;ys|] -> List.zip xs ys | _ -> []
    |> readBoard

let test input expected =
    let result = if (solve input) = expected then "OK" else "NG"
    printfn "%s" result

[<EntryPoint>]
let main argv = 
    test "2ed8aeed/34b0ea5b" "LTRSUNTSJ"
    test "00000200/00000300" "L"
    test "00018000/00010000" "R"
    test "00002000/00006000" "J"
    test "00000700/00000200" "T"
    test "01400000/01c00000" "U"
    test "00003800/00002800" "N"
    test "000c0000/00180000" "S"
    test "00003000/00001800" "Z"
    test "132eae6c/1a64eac6" "LRJTUNSZ"
    test "637572d0/36572698" "ZSNUTJRL"
    test "baddb607/d66b6c05" "LTJZTSSSN"
    test "db74cd75/6dac6b57" "ZZZTJZRJNU"
    test "3606c2e8/1b0d8358" "ZZSSLTJ"
    test "ad98c306/e6cc6183" "UZZZZZZ"
    test "4a4aaee3/db6eeaa6" "JJLLUUNNS"
    test "ecd9bbb6/598cd124" "TSSZZTTRR"
    test "e0000002/40000003" "TL"
    test "a0000007/e0000005" "UN"
    test "c0000003/80000006" "RS"
    test "40000006/c0000003" "JZ"
    test "01da94db/00b3b6b2" "TSUJLRSR"
    test "76eeaaea/24aaeeae" "TRNNUUNU"
    test "1dacaeee/1566e444" "NRJZUTTT"
    test "26c9ac60/6c6d66c0" "JSZLRJZS"
    test "6c977620/36da5360" "ZZLLTNZJ"
    test "069aeae6/0db34eac" "SJSLTUNS"
    test "06d53724/049da56c" "RRULRNJJ"
    test "069b58b0/04d66da0" "RLRSLZJR"
    test "1b6eced4/11b46a9c" "RZZTZNRU"
    test "522e8b80/db6ad900" "JLLJNLJT"
    test "6546cdd0/376c6898" "ZULSZRTL"
    test "4e6d5b70/6ad9d620" "LNSSURST"
    test "37367772/65635256" "SNSZNTNJ"
    test "25535d58/377669cc" "LUUSLTUZ"
    test "0ae6a55d/0eacedcb" "UNSUJUTJ"
    test "76762edc/23536a88" "TZNZJNRT"
    0
```