osiire’s blog

ふしぎなそふとやさん

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