2012-03-15 66 views
3

我有一组输入,我想限制它的输入。我希望它的行为如下:在OCaml中改变模块行为

# RestrictedIntSet.add 15 (RestrictedIntSet.make 0 10) 
Exception: 15 out of acceptable range [0 .. 10] 

我该如何执行此操作?在Java中,它可能看起来像:

Set<Integer> restrictedSet = new HashSet<Integer>() { 
    public boolean add(Integer i) { 
     if (i < lowerBound || i > upperBound) { 
      throw new IllegalArgumentException("out of bounds"); 
     } 
     return super.add(i); 
    } 

或者是继承的滥权少:

public class RestrictedSet { 

    private int lowerBound; 
    private int upperBound; 
    private Set elems = Sets.newHashSet(); 

    public RestrictedSet(int lowerBound, int upperBound) { 
     this.lowerBound = lowerBound; 
     this.upperBound = upperBound; 
    } 

    public boolean add(Integer i) { 
     if (i < lowerBound || i > upperBound) { 
     throw new IllegalArgumentException("out of bounds"); 
     } 
     return elems.add(i); 
    } 

    /* fill in other forwarded Set calls as needed */ 
} 

什么是等价的,惯用的方式OCaml中做到这一点?

回答

7

那么,这取决于你使用哪个set库?

使用Set模块的标准库,你可以做到以下几点:

module type RestrictedOrderedType = sig 
    type t 
    val compare : t -> t -> int 
    val lower_bound : t 
    val upper_bound : t 
end 

module RestrictedSet (Elem : RestrictedOrderedType) = struct 
    include Set.Make(Elem) 

    exception Not_in_range of Elem.t 

    let check e = 
    if Elem.compare e Elem.lower_bound < 0 
    || Elem.compare e Elem.upper_bound > 0 
    then raise (Not_in_range e) 
    else e 

    (* redefine a new 'add' in term of the one in Set.Make(Elem) *) 
    let add e s = add (check e) s 

    let singleton e = singleton (check e) 
end 


(* test *) 
module MySet = RestrictedSet(struct 
    type t = int 
    let compare = compare 
    let lower_bound = 0 
    let upper_bound = 10 
end) 

let test1 = MySet.singleton 3 

let test2 = MySet.add (-3) test1 
(* Exception: Not_in_range (-3) *) 
2

我喜欢@ gasches的答案。

作为一个简短补充:OCaml的Set模块被设计为由OrderedType模块实例化,这意味着您不能直接直接使用OCaml的原生int s。

因此需要使用符合请求签名的模块。 gasche对RestrictedOrderedType签名的定义是否优雅地包含下限和上限字段。更粗略的方法是使用OCaml的Int32或Int64模块,它符合请求的OrderedType签名,并对MySet模块中的边界进行硬编码。

下面是gasche举例说明这一点的一个小小的改写。

module MySet = struct 
    include Set.Make(Int32) 

    exception Not_in_range of Int32.t 

    let lower_bound = Int32.of_int 5 

    let upper_bound = Int32.of_int 10 

    let add elt set = 
     if (elt < lower_bound)||(elt > upper_bound) 
     then raise (Not_in_range elt) 
     else add elt set 

    end;;