さて、ようやくのこと、 F# で Fizz-Buzz 問題を "わざわざ" アクティブパターンを使って "変則的に" 解くときがやってきた。
実は元ネタはこちら。
Rainy Day Codings - 雨の日のプログラミング-
「F# の Active Pattern (で FizzBuzz)」
http://rainyday.blog.so-net.ne.jp/2007-05-25
# ちなみに、3年ほど前の記事である。それを現在追いかけているところであるわけだ。
「if ~ elif ~ ... とか書くくらいなら、C# の switch よろしく、パターンマッチでガード条件で列記したほうがすっきりするね」
「でも、それだと、本当に C# の switch 代わりにパターンマッチの構文をあてがってるだけで、実はぜんぜんパターンマッチじゃないね」
「剰余がゼロなら...という判定式も、3の倍数ならFizz、という命題に対して直接的じゃないよ」
という発想から、
「パターンマッチのガード条件に、"5になにかを掛けたもの" って記述できたら、自然じゃない?」
という発想に至ったそうだ。
すなわち、こんな風に FizzBuzz 関数を書けたら... ということである。
let FizzBuzz = fun x ->
match x with
|15 * _ -> "FizzBuzz"
|3 * _ -> "Fizz"
|5 * _ -> "Buzz"
|_ -> x.ToString()
ガード条件の 「15 * _」が、「15 掛ける何か("_")」を表しており、すなわち、FizzBuzz 関数の引数 x が「15 掛ける何か("_")」にマッチしたら "FizzBuzz"、という寸法である。
しかしもちろん、このような記述はできない。
その代わり、アクティブパターンを応用すれば少しは上記の雰囲気に近づけるよ、という話である。
さて実はもともとは元ネタの記事を単純に本ブログでもなぞって終わりにしようと考えていた。
しかし、前回でパラメタライズドなアクティブパターンは、"非パーシャルな" 通常のアクティブパターンでも書けることがわかった。
そこで、元ネタのパラメタライズドパーシャルアクティブパターンは使わずに、パラメタライズド非パーシャルアクティブパターンで実装してみることにする。
まず、マッチさせようとしている値が "何か" の倍数であれば true となるような、パラメタライズドアクティブパターンを定義する。
ここでいう "何か" がパラメータとなる。
> let (|XisMultipleOf|) divisor x = x % divisor = 0;;
これで、x が divisor の倍数であれば true、そうでなければ false が返るパラメタライズドアクティブパターンが定義できた。
divisor に 5 を指定すれば、5の倍数なら true を返すアクティブパターンのできあがりだ。
次に、このパラメタライズドアクティブパターンを用いて、Fizz-Buzz の判定を行う FizzBuzz 関数を定義する。
>let FizzBuzz = fun x ->
> match x with
> |XisMultipleOf 15 true -> "FizzBuzz"
> |XisMultipleOf 3 true -> "Fizz"
> |XisMultipleOf 5 true -> "Buzz"
> |_ -> x.ToString();;
ガード条件「XisMultipleOf 15」が「x が 15 の倍数である場合」を表しているわけだ。
ただし、アクティブパターン「XisMultipleOf 15」の戻り値を考慮しなくてはならないため、これが true である場合も付記して「XisMultipleOf 15
true」と記述する必要があるのが、ちょっと苦しい。
とはいえ、以上で、
>List.map FizzBuzz [1..100];;
と実行してやれば、見事 Fizz-Buzz 問題の解となる。
ここまで来るのは長かった~...