GoF in OCaml for Advent Calendar 2012 #8
OCaml Advent Calendar用の記事、第八弾目です。commandパターンいってみましょう。
http://en.wikipedia.org/wiki/Command_pattern
上記のリンクは英語版です。(日本語版にはサンプルコードが載ってなかったので。@mrgchrさん情報ありがとうございます!)
コマンドパターンの「コマンド」とは、何か実行したい処理とそのパラメーターをカプセル化したものです。そのオブジェクトのコレクションを扱う事で、undoを統一的に処理したり、優先順位を付けて処理したり、後で再生して利用したりする事ができるようになるらしいです。
とりあえず例として載っているコードをOCaml化してみましょう。
(* コマンドが持つべき関数. これのコレクションを扱う. *) type command = { execute : unit -> unit; } module Switch = struct type t = command Queue.t (* OCamlの標準ライブラリにあるキューは破壊的. *) let make () = Queue.create () let storeAndExecute t command = command.execute (); (* コマンドを実行して、キューに入れておく. *) Queue.push command t end module Light = struct type t = unit (* 何か明かりを表現するものだけど、今回は省略 *) let make () = () let turnOn t = Printf.printf "the light is on\n" let turnOff t = Printf.printf "the light is off\n" end module FlipUp = struct type t = Light.t let make t = t let execute t = Light.turnOn t let command t = { (* FlipUp.tからcommandレコードの値を作る. *) execute = fun () -> execute t } end module FlipDown = struct type t = Light.t let make t = t let execute t = Light.turnOff t let command t = { execute = fun () -> execute t } end let _ = let lamp = Light.make () in let switchUp = FlipUp.command (FlipUp.make lamp) in let switchDown = FlipDown.command (FlipDown.make lamp) in let switch = Switch.make () in match Sys.argv.(1) with | "ON" -> Switch.storeAndExecute switch switchUp | "OFF" -> Switch.storeAndExecute switch switchDown | _ -> ()
共通する部分をレコードとして切り出して使う構造としては、Proxyパターンと殆ど一緒です。
Switchモジュールが持つコマンド履歴が破壊的なところに注目してください。こういう状態を示すものはOCamlでも破壊的に扱う事がよくあります。もちろんStateモナドにしても構いませんが、プログラム全体がデータフローで完結するとき以外は使わないかもしれません。
次回に続く!
GoF in OCaml for Advent Calendar 2012 #8
OCaml Advent Calendar用の記事、第八弾目です。commandパターンいってみましょう。
http://en.wikipedia.org/wiki/Command_pattern
上記のリンクは英語版です。(日本語版にはサンプルコードが載ってなかったので。@mrgchrさん情報ありがとうございます!)
コマンドパターンの「コマンド」とは、何か実行したい処理とそのパラメーターをカプセル化したものです。そのオブジェクトのコレクションを扱う事で、undoを統一的に処理したり、優先順位を付けて処理したり、後で再生して利用したりする事ができるようになるらしいです。
とりあえず例として載っているコードをOCaml化してみましょう。
(* コマンドが持つべき関数. これのコレクションを扱う. *) type command = { execute : unit -> unit; } module Switch = struct type t = command Queue.t (* OCamlの標準ライブラリにあるキューは破壊的. *) let make () = Queue.create () let storeAndExecute t command = command.execute (); (* コマンドを実行して、キューに入れておく. *) Queue.push command t end module Light = struct type t = unit (* 何か明かりを表現するものだけど、今回は省略 *) let make () = () let turnOn t = Printf.printf "the light is on\n" let turnOff t = Printf.printf "the light is off\n" end module FlipUp = struct type t = Light.t let make t = t let execute t = Light.turnOn t let command t = { (* FlipUp.tからcommandレコードの値を作る. *) execute = fun () -> execute t } end module FlipDown = struct type t = Light.t let make t = t let execute t = Light.turnOff t let command t = { execute = fun () -> execute t } end let _ = let lamp = Light.make () in let switchUp = FlipUp.command (FlipUp.make lamp) in let switchDown = FlipDown.command (FlipDown.make lamp) in let switch = Switch.make () in match Sys.argv.(1) with | "ON" -> Switch.storeAndExecute switch switchUp | "OFF" -> Switch.storeAndExecute switch switchDown | _ -> ()
共通する部分をレコードとして切り出して使う構造としては、Proxyパターンと殆ど一緒です。
Switchモジュールが持つコマンド履歴が破壊的なところに注目してください。こういう状態を示すものはOCamlでも破壊的に扱う事がよくあります。もちろんStateモナドにしても構いませんが、プログラム全体がデータフローで完結するとき以外は使わないかもしれません。
次回に続く!
GoF in OCaml for Advent Calendar 2012 #7
OCaml Advent Calendar用の記事、第七弾目です。今日から振る舞いに関するパターンシリーズに突入です。最初はChain of Responsibilityパターンいってみましょう。
http://ja.wikipedia.org/wiki/Chain_of_Responsibility_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
Chain of Responsibilityは特定のデータを処理の鎖に流し込む手法です。どのような順番で鎖を繋げるか、鎖の途中でデータの流れを止めるか止めないか、その辺りの変更で動的な処理の流れが構築できる訳です。
OCamlでChain of Responsibilityするなら(t -> t)の関数を合成するのが最も単純な戦略だと思います。途中で処理を破棄したい場合は、(t -> t option)の関数を使ってoption bindする手もあるでしょう。Wikipediaの例が単純な例なので、そちらに合わせたプログラムは下記のようになります。
type priority = ERR | NOTICE | DEBUG type message = string * priority let (+>) f g = g f let tee f x = f x; x (* ロガーの元となる関数. これを特殊化していく. *) let logger writeMessage mask ((msg, priority) as v) = tee (fun _ -> if priority <= mask then (* 実はバリアントは大小比較できる. 先に定義された方が小さい. *) writeMessage msg) v let stdoutLogger mask ((msg, _) as v) = logger (fun msg -> print_string ("writing to stdout: " ^ msg ^ "\n")) mask v let emailLogger mask ((msg, _) as v) = logger (fun msg -> print_string ("sending via email: " ^ msg ^ "\n")) mask v let stderrLogger mask ((msg, _) as v) = logger (fun msg -> print_string ("sending to stderr: " ^ msg ^ "\n")) mask v let _ = let log x = (* 関数の鎖を作る. *) stdoutLogger DEBUG x +> emailLogger NOTICE +> stderrLogger ERR +> (fun _ -> ()) (* 最後の戻り値をunitにするための壁. *) in log ("Entering funciton y", DEBUG); log ("Step1 complete", NOTICE); log ("An error has occurred", ERR) (* 実行結果 writing to stdout: Entering funciton y writing to stdout: Step1 complete sending via email: Step1 complete writing to stdout: An error has occurred sending via email: An error has occurred sending to stderr: An error has occurred *)
OCamlのコードの興味深い点は、鎖を作る事は(+>)演算子によって構成されており、logger関数とは分離されている点です。Javaの例ではsetNextがLoggerクラスと強く結びついていました。関数の糊付けは関数プログラミングの得意とするところです。
次回に続く!
GoF in OCaml for Advent Calendar 2012 #6
OCaml Advent Calendar用の記事、第六弾目です。構造に関するパターンの最後、Proxyパターンやります。
http://ja.wikipedia.org/wiki/Proxy_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
Proxyパターンは、インターフェイス経由で実体にアクセスすることで、間に一つ中間層を入れ込もうというパターンです。他のパターンでもそうでしたが、JavaでのインターフェイスはOCamlではレコードにするとよいでしょう。
(* おまじない的補助関数. *) let print s = Printf.printf "%s\n" s let tee f x = f x; x let (+>) f g = g f (* Imageインターフェイスの代わり. 共通の操作を列挙する. *) type image = { display : unit -> unit; } module Image = struct type t = { fname : string; } (* 何か画像を表現するデータは省略.*) let loadImageFromDisk fname = (* なにか時間のかかるロード処理. *) print ("loading " ^ fname) let make fname = loadImageFromDisk fname; { fname } let display t = print ("displaying " ^ t.fname) (* 共通操作を抜き出す. *) let writer t = { display = fun () -> display t } end module ProxyImage = struct type t = { fname : string; (* Image.makeに渡す引数を取っておく. *) mutable cache : Image.t option; (* キャッシュ. *) } let make fname = { fname; cache = None } (* 共通操作を抜き出す. *) let writer t = { display = (fun () -> let image = match t.cache with | None -> (* キャッシュがなければ生成. *) Image.make t.fname +> tee (fun i -> t.cache <- Some i) | Some image -> image in Image.display image) } end let _ = let image1 = ProxyImage.writer (ProxyImage.make "HiRes_10MB_Photo1") in let image2 = ProxyImage.writer (ProxyImage.make "HiRes_10MB_Photo2") in let image3 = ProxyImage.writer (ProxyImage.make "HiRes_10MB_Photo3") in image1.display (); (* ここでロードされる. *) image2.display (); image2.display () (* ロード不要で表示.*) (* image3はロードされずに破棄される. *)
第一級のモジュールでやってもできますが、その場合には型tとdisplay関数を持つシグネチャーを定義する事になるでしょう。その型tがImage.tとProxyImage.tの二種類の型に対応する訳です。しかし、この型tには特に意味がない(他の型とユニファイする訳でもなく、戻り値の型になる訳でもない)ので、実際不要です。そこでdisplay関数だけのレコードで十分という判断になります。
レコードには型を隠す意味もあるんですね。
次回に続く!-> http://d.hatena.ne.jp/osiire/20121213
GoF in OCaml for Advent Calendar 2012 #5
OCaml Advent Calendar用の記事、第五弾目です。Flyweight パターンやります。
http://ja.wikipedia.org/wiki/Flyweight_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
Flyweightパターンはどうやらインスタンスをキャッシュして使いまわす手法のようです。OCamlでキャッシュを使ってインスタンスを使いまわすなら、メモ化関数を作って利用するのが一般的かと思います。
(* メモ化関数. 同じ引数にはキャッシュしておいた値を返す. *) let memoize cache proc args = match try Hashtbl.find cache args with Not_found -> let v = try `Val (proc args) with e -> `Err e in Hashtbl.replace cache args v; (* Hashtblモジュールは破壊的. *) v with `Val x -> x | `Err e -> raise e module Stamp = struct (* Wikipediaの例ではchar型でUNICODE文字を扱っている. *) (* OCamlでUNICODE文字を扱うにはulibとか使う必要があるので、今回は文字列に. *) type t = string let make c = c let print c = print_string c end module StampFactory = struct let cache = Hashtbl.create 10 let clear () = Hashtbl.clear cache (* キャッシュをクリアできるように. *) let get = (* get関数が使うローカル関数を呼ばれる前に作るテクニック. *) (* 最後にfun c -> ..という関数を返している. getが関数である事がよくわかる. *) let mget = memoize cache Stamp.make in fun c -> mget c end (* 使い方 *) let _ = List.iter Stamp.print [StampFactory.get "た"; StampFactory.get "か"; StampFactory.get "い"; StampFactory.get "た"; StampFactory.get "け"; StampFactory.get "た"; StampFactory.get "て"; StampFactory.get "か"; StampFactory.get "け"; StampFactory.get "た"; ]
特にコメントなし。メモ化と呼ぶかFlyweightパターンと呼ぶか、名称の違いかと。
次回に続く! -> http://d.hatena.ne.jp/osiire/20121212
GoF in OCaml for Advent Calendar 2012 #4
OCaml Advent Calendar用の記事、第四弾目です。今日はFacadeパターンいってみましょう。
http://ja.wikipedia.org/wiki/Facade_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
Facadeパターンは、関連するクラス群を使用するための手続きを、窓口となる一つのクラスに集約するらしいです。
- 関連するクラス群を外部から隠蔽するためにパッケージスコープでアクセス制限する。
- Facadeクラスに記述された処理が、コードの重複を防いだり処理の見通しを良くしてくれる。
OCamlにはパッケージ名前空間はないので(2012年冬現在)、外部から隠蔽したい関連モジュールはFacadeモジュールに含めればいいでしょう。
module DrivingSimulator : sig (* 外部にはsimulate関数しか見せない. *) val simulate : unit -> unit end = struct external (|>) : 'a -> ('a -> 'b) -> 'b = "%revapply" (* 4.00.0未満は次のコードを使いましょう. *) (* let (|>) f g = g f *) (* モジュール内モジュール. * DrivingSimulatorのシグネチャーに記述されていないので、外部からは見えない. *) module Car = struct type t = { speed : int; distance : int } let make speed distance = { speed; distance; } (* 破壊的代入はしない。tが引数の最後にある事に注目 *) let run minutes t = { t with distance = t.distance + minutes * t.speed } let setSpeed speed t = { t with speed = speed } let distance t = t.distance end module Driver = struct type t = Car.t let ride car = car let pushPedal speed t = Car.setSpeed speed t let drive minutes t = Car.run minutes t end let simulate () = (* パイプライン演算子で次々と処理を繋げる。 * CarやDriverに破壊的代入をせずに書くときはこういう書き方をする. *) Driver.ride (Car.make 0 0) |> Driver.pushPedal 700 |> Driver.drive 30 |> Driver.pushPedal 750 |> Driver.drive 20 |> (fun car -> Printf.printf "The travel distance is %d m." (Car.distance car)) end
CarやDriverに破壊的代入が使われていない点に注目してください。破壊的代入の代わりに、setSpeedやpushPedalは新しいCar.tやDriver.tを返しています。こういう新しい値を返す関数の場合、元の値を引数の最後に持ってきます。そうすると、simulate関数にあるようなパイプ演算子で繋げる関数プログラミングっぽいスタイルで記述できるようになります。
これパターンって呼んでいいのかな・・・。まぁいいか。
次回に続く! -> http://d.hatena.ne.jp/osiire/20121210
GoF in OCaml for Advent Calendar 2012 #3
三日坊主の三日目です。これで目標は果たせるので一安心です。
今日は構造パターンの一つDecoratorパターンやってみます。
http://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
Decoratorパターンは、特定の(Javaで言う)インターフェイスを満たすオブジェクトを受け取ってそれを拡張する事を目指したものです。インターフェイス経由で拡張するので色々なクラスのオブジェクトを拡張できます。
これをOCaml風に書き直すならば、次のようなコードになると思います。
(* Priceが満たすべき関数を定義 *) type price = { getValue : unit -> int; } (* 基礎的な価格付け *) let primePrice v = { getValue = fun () -> v; } (* 利益上乗せ価格 *) let wholesalePrice p advantage = { getValue = fun () -> p.getValue () + advantage; } (* 原価の二倍価格 *) let doublePrice p = { getValue = fun () -> (p.getValue ()) * 2 } (* 使い方 *) let _ = print_int ((wholesalePrice (doublePrice (wholesalePrice (doublePrice (primePrice 120)) 80)) 200).getValue ())
Priceインターフェイスがpriceレコード型に対応している点に注目してください。
特定の状態を内包するオブジェクト(インスタンス)を拡張するのがDecoratorパターンなので、OCamlでは状態を内包する高階関数を持つレコードに対応付けています。priceレコードをPriceモジュールにする事もできますが、OCamlでは第一級のモジュールを扱う文法はレコードを扱う文法より煩雑なので、型を持ち運ぶ必要がない限り使いません。
WholesalePriceモジュールを作るとしたら、次のような感じになります。
(* 利益上乗せ価格を扱うモジュール *) module WholesalePrice = struct type t = price * int (* インスタンスの生成 *) let make p advangate = p, advantage (* priceインターフェイスを持つインスタンスの生成 *) let makePrice p advantage = { getValue = fun () -> p.getValue () + advantage } end
price型の値を生成する関数を別途makePriceとして定義しています。モジュールには(通常は)状態を持たせないので、このような設計になります。
Javaでいうクラスとオブジェクト、OCamlのレコードとモジュールの使い方の違いが鮮明になって面白い例かも知しれません。
次回に続く! -> http://d.hatena.ne.jp/osiire/20121206