2012-03-15 65 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;;