改变OCaml模块行为

3

我有一组整数,想要对其输入进行限制。我希望它的行为类似于以下内容:

# 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中有什么等效且惯用的方式吗?
2个回答

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。因此,需要使用符合请求签名的模块。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;;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接