もっとアクティブパターンでFizzBuzz

先日のアクティブパターンでFizzBuzzで書いた内容は今思うとあまりアクティブパターンのよさを引き出していないかも。
もっとアクティブパターンぽい方法を思いつきました。

[<Test>]
let ``fizzbuzz with partial active pattern``() =
let (|Fizz|_|) n = if n % 3 = 0 then Some("fizz") else None
let (|Buzz|_|) n = if n % 5 = 0 then Some("buzz") else None
let fizzbuzz = function
| Fizz(x) & Buzz(y) -> x + y
| Fizz(x) | Buzz(x) -> x
| x -> string x

seq {1 .. 100} |> Seq.map fizzbuzz |> Seq.iter(printfn "%s")

肝はもちろんfizzbuzz関数。パターンマッチングでFizzかつBuzzのときはそれぞれxとyにバインディングし、FuzzまたはBuzzのときはどちらもxにバインディングしているのがポイントです。FizzかつBuzzのときはマッチングした文字列を連結して、FuzzまたはBuzzのときはマッチングできた文字列をそのまま返します。FizzでもBuzzでもないときは、文字列に変換しています。stringはstringに変換する関数。

追記 1/3

もう少しきれいに。余分な括弧消したり、商を求めるところを宣言的にしてみたり。

[<Test>]
let ``fizzbuzz with partial active pattern``() =
let (|MultipleOf|_|) n inp = if inp % n = 0 then Some () else None
let (|Fizz|_|) = function MultipleOf 3 -> Some "fizz" | _ -> None
let (|Buzz|_|) = function MultipleOf 5 -> Some "buzz" | _ -> None

let fizzbuzz =
function
| Fizz x & Buzz y -> x + y
| Fizz x | Buzz x -> x
| x -> string x

seq { 1 .. 100 } |> Seq.map fizzbuzz |> Seq.iter (printfn "%s")