So-net無料ブログ作成

F# の Active Pattern (で FizzBuzz) [OCaml]

F# にはアクティブパターン (active pattern) という機能があって以前ここで書いた [1] Scala の extractor のようなことができるようになっています。リリースは Scala よりも後ですが、もともと F# 開発者の Don Syme さんのアイデアに Scala 開発者の Martin Odersky さんが多大な興味を示し、それぞれの現われが active pattern と extractor ということのようです。F# の方は何か分かりにくさがあって理解しきれていないかもしれませんが、ちょっと紹介してみます。

例として少し前までプログラミング系ブログを席巻していた Fizz-Buzz 問題 [2] を取り上げます。
まず、心のきれいな OCaml プログラマが変な方向に凝ろうとせずに素直に Fizz-Buzz 問題を書き下したら、おそらく大体以下のようなものになるのではないかと思います。

let fizzbuzz = function
| n when n mod 15 = 0 -> "FizzBuzz"
| n when n mod  3 = 0 -> "Fizz"
| n when n mod  5 = 0 -> "Buzz"
| n -> string_of_int n
;;

for i = 1 to 100 do print_string (fizzbuzz i ^ " ") done

これはこれでもう動かしようがないと個人的に思いますが、引っかかりを残すのはパターンマッチの部分です。上記のコードでは関数の引数を「パターン」に「マッチ」させているといえる部分はまったくなくて、実質的な仕事をしているのはガード条件の部分です。単に3つ以上の分岐を書く構文が他にないので使っているにすぎません。ついでに言えば「N の倍数である」ということを言うのに「N で割った余りが 0 である」といちいち言い換えなければいけない部分も気に入りません。本当は fizzbuzz 関数を以下のように書けたらいいのではないのでしょうか。

let fizzbuzz = function
| 15 * _ -> "FizzBuzz"
|  3 * _ -> "Fizz"
|  5 * _ -> "Buzz"
| n -> string_of_int n
;;

F# でアクティブパターンを使えば(全く上記のとおりには書けないけど)できるよ、というのが今日の話です。

こんな風に書きます。

#nowarn "57";;

let (|Mul|_|) x y = if y % x = 0 then Some(y / x) else None;;

let fizzbuzz = function
| Mul 15 _ -> "FizzBuzz"
| Mul  3 _ -> "Fizz"
| Mul  5 _ -> "Buzz"
| n -> string_of_int n
;;

for i = 1 to 100 do print_string (fizzbuzz i ^ " ") done

let (|Mul|_|) ~というのが問題の active pattern を定義している部分です。(| |) という記号はバナナに似ているので banana marks とか banana symbols とか呼ばれているようです。定義された ( |Mul|_| ) は見かけは変ですが普通に関数として使えます。

> ( |Mul|_| ) 3 15;;
val it : int option = Some 5
> ( |Mul|_| ) 5 15;;
val it : int option = Some 3
> ( |Mul|_| ) 7 15;;
val it : int option = None

特殊なのは、この関数はパターンマッチのパターン中で Mul が出てきたときに Mul の逆適用関数として呼び出されるという点です。その逆適用の結果の戻り値が Some 何とかであった場合はパターンマッチ成功でその何とかにパターン中の変数が束縛され、None であった場合はパターンマッチ失敗とみなされるというわけです。

( |Mul|_| ) の中ごろの余計なアンダーバーとパイプはこの関数がオプション型を返す場合の書き方です。これは partial active pattern と呼ばれます。(|Mul|) とか書くときは逆適用が常に成功するようなパターンを定義することになります。
また ( |Mul|_| ) は2つ引数を取りますが、これは parameterized active pattern と呼ばれるもので、おそらく Scala にはこれに相当する機能はないと思います。分かりにくいですが Mul 15 _ と書くときの 15 と _ は同じ立場のものではなくて、実は 15 は Mul をパラメタライズしているだけです。なのでパターンマッチ中に Mul _ 15 とはかけません。
なお parameterized active pattern の構文はまだ確定とはされていないようで、使うと警告が出ます。これを抑止するために #nowarn "57";; という行を書いています。

[1] http://blog.so-net.ne.jp/rainyday/2007-02-26
[2] http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm


nice!(0)  コメント(5)  トラックバック(1) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 5

siokoshou

おもしろい機能ですね!
逆適用の逆の意味がわかりませんでした。普通に適用した結果の戻り値を見ているだけのように思えるのですが…。どうして「逆」適用と呼ばれているのでしょうか?
by siokoshou (2007-07-25 09:57) 

ether

逆適用という言葉を使ったのは Scala では関数オブジェクトに apply メソッドと unapply というメソッドを(基本的には逆関数のようになるように)ペアで定義して、その unapply メソッドが F# でいう active pattern に相当するというところから言葉をつい借りたのですが、F# だとそういうわけではないので逆という言葉は迂闊でした。うまく当てはまっていないし特に F# の用語でもないと思いますので良くなかったですね。
by ether (2007-07-25 22:51) 

siokoshou

なるほど~、勉強になります。どうもありがとうございます!
by siokoshou (2007-07-26 01:32) 

いげ太

はじめまして。古い記事へのコメント失礼します。
いいですね、アクティブ パターン!僕も勉強のためにアクティブ パターンに関するエントリを書いてみたのですが、ether さんのエントリをすごく参考にさせていただきました。コードもちょっとお借りさせていただいたので、ご報告をと思いコメントさせていただきました。
by いげ太 (2008-04-03 22:27) 

ether

言及ありがとうございます。
どうもこの記事を書いたときは Scala になぞらえようとしすぎていた感があって、コード例も partial active pattern だけだったりするのですが、いげ太さんの記事のように |_ なしから説明して |_ ありに入ったほうが順序良くて分かりよいですよね。
by ether (2008-04-04 23:19) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 1