アクティブパターンでFizzBuzz
アクティブパターンを使いこせるとかっこいいですよね。アクティブパターンでFizzBuzzは目新しい話ではなさそうですが、やってみました。
アクティブパターンを使わない例。
まずは普通に。fizzbuzz関数の中で条件分岐してます。
[<Test>]
let ``fizzbuzz without Active Pattern`` () =
let fizzbuzz = function
| n when n % 15 = 0 -> "fizzbuzz"
| n when n % 3 = 0 -> "fizz"
| n when n % 5 = 0 -> "buzz"
| n -> string n
seq {1 .. 100}
|> Seq.map fizzbuzz
|> Seq.iter (printfn "%s")
とりあえずアクティブパターンを使ってみた例。
FizzBuzzがアクティブパターン。この例だとありがたみがないです。
Seq.mapへのラムダ関数でアクティブパターンの結果を取得していますが、この記述方法はちょっとわかりにくい。慣れの問題だと思いますが。xにFizzBuzzの戻り値がバインディングされます。
[<Test>]
let ``fizzbuzz with Active Pattern`` () =
let (|FizzBuzz|) = function
| n when n % 15 = 0 -> "fizzbuzz"
| n when n % 3 = 0 -> "fizz"
| n when n % 5 = 0 -> "buzz"
| n -> string n
seq {1 .. 100}
|> Seq.map (fun (FizzBuzz x) -> x)
|> Seq.iter (printfn "%s")
0で除算するコードの重複を排除する形でアクティブパターンを使用した例。
F# の Active Pattern (で FizzBuzz) [OCaml]を参考にさせてもらいました。アクティブパターンへの引数がどれで戻り値がどれ?というのがわかりにくいです。慣れるしかないって感じでしょうか。この例だと、fizzbuzz関数への引数がMulのdividendになって、Mulを呼び出しているときに15や3や5を渡していますがこれがMulのmodulusになります。そして結果のSomeの要素が、_ に戻されます。
[<Test>]
let ``fizzbuzz with Partial and Parameterized Active Pattern`` () =
let (|Mul|_|) modulus dividend =
if dividend % modulus = 0 then Some(dividend / modulus) else Nonelet fizzbuzz = function
| Mul 15 _ -> "fizzbuzz"
| Mul 3 _ -> "fizz"
| Mul 5 _ -> "buzz"
| n -> string nseq {1 .. 100}
|> Seq.map fizzbuzz
|> Seq.iter (printfn "%s")
本題とはずれますが、functionキーワードを使うべきか迷います。今回は使えるところは全部つかってみました。
たとえば、最初のケースのfizzbuzz関数ですが、こう書いても同じなんですよね。
let fizzbuzz n =
if n % 15 = 0 then "fizzbuzz"
elif n % 3 = 0 then "fizz"
elif n % 5 = 0 then "buzz"
else string n