为堆栈和队列提取通用接口

时间:2013-12-15 23:14:52

标签: polymorphism ocaml functor

我想比较一下我正在研究的游戏AI的两种启发式的性能。一个处理FIFO顺序的元素;另一个是LIFO订单。

目前,代码使用Queue以FIFO顺序处理元素。在OOP中,BagStack都会实现Queue接口,我会使用Bag.pushBag.take与结构进行交互。然后,我会根据命令行标志的值,将Stack.create ()Queue.create ()的结果分配给类型Bag的变量。

我知道如何使用抽象类复制OCaml中的类似行为并使用:>约束类型。但是,我想可能有一种更简洁的方法可以做到这一点,不需要在QueueStack周围编写包装类。

我试过像

这样的东西
module type Bag =
sig
    type 'a t
    exception Empty
    val create : unit -> 'a t
    val push : 'a -> 'a t -> unit
    val iter : ('a -> unit) -> 'a t -> unit
end

module BagWrapper (B : Bag) = 
struct
    type 'a t = 'a B.t
    let create () = B.create ()
    let push a b = B.push a b
    let iter a b = B.iter a b
end 

module QueueBag = BagWrapper(Queue)
module StackBag = BagWrapper(Stack)

......但我不确定接下来该做什么。想法?

2 个答案:

答案 0 :(得分:3)

使用一流模块决定“动态”的ygrek解决方案 (在布尔值上)Bag模块的值很好,但您也可以这样做 通过简单地定义遍历算法,在更简单的OCaml中 在一个以袋子为参数的仿函数里面。

module type Bag =
sig
    type 'a t
    exception Empty
    val create : unit -> 'a t
    val push : 'a -> 'a t -> unit
    val pop : 'a t -> 'a
    val iter : ('a -> unit) -> 'a t -> unit
end

module Traversal (B : Bag) = struct
  let go start children f =
    let to_visit = B.create () in
    B.push start to_visit;
    try while true do
      let next = B.pop to_visit in
      f next;
      let mark neighbor = B.push neighbor to_visit in
      List.iter mark (children next);
    done with B.Empty -> ()
end;;

(* note that "BagWrapper" is useless here as the existing modules
   fit the Bag interface perfectly, there is no need for a middle
   layer *)
module StackTraversal = Traversal(Stack)
module QueueTraversal = Traversal(Queue)


(* ... if foo then StackTraversal.go else QueueTraversal.go ... *)

答案 1 :(得分:2)

好的,所以你要写

let module Bag = if use_queue then QueueBag else StackBag in
some algorithm

但OCaml不允许你这样做..

First-class modules救援!

let m = if use_queue then (module QueueBag : Bag) else (module StackBag : Bag) in
let module Bag = (val m : Bag) in

这看起来相当冗长,幸运的是,较新的OCaml版本允许您在这种情况下删除某些类型的注释。