在OCaml中有没有一种方法可以在记录中声明一个整数,这个整数是一些整数的区间?

问题描述 投票:0回答:1

我在这个问题上卡住了,我必须:将日期类型定义为一个三段式,其中第一段是一个用整数表示的年份,第二段是一个来自[1...12]区间的整数,表示月份,最后一段是用一个整数表示一天。

type date = { year: int; month: int; day:int};;

{year = 2012; month = 12; day = 21};;

这很好用,但是对于参数月份,我需要有1到12的整数作为输入。

ocaml
1个回答
1
投票

如果你想对月份使用一个变体并列举所有的月份。你可以对日数做同样的处理,但我会避免这样做。

如果你想要一个更普遍的概念,如范围类型:你不希望,因为月有30或31天,二月有28-29取决于闰年,所以它是依赖于范围...... 如果这个检查是可用的,那么你想要哪个范围?你所需要的检查是非常具体的:作为一个例子,我只在cpp中看到过这个扩展,你被迫从范围中使用int(所以非常有用)。因为这个扩展需要静态的int,所以你不能真正的使用日期。OCaml不能这么做,因为所有的int都是 int 而不是30或31。通常的变通方法是创建一个函数,取三个int验证它们是否在有效范围内,然后返回 date option 并且也让它从外部私有化,这样你就不能破坏这个不变式。我们称之为智能构造函数。

module SafeDate : sig
    type date = private { year: int; month: int; day:int}
    val create: int -> int -> int -> date option
end = struct
    type date = { year: int; month: int; day:int}
    let create year month day = if (* put formula/code to say if correct *) then Some{year; month; day} else None
end

0
投票

不,但你可以防止意外。

OCaml(和其他地方)的时间和日期库通常会确保日期对象的组件落在正确的范围内,而不需要特殊的类型系统技巧。

在OCaml中,一个好的解决方案可能是将日期的字段暴露为只读。这意味着模块的用户将无法使用符号来创建这种类型的记录。{year=...; month=...; day=...}. 这是通过提供一个显式模块接口(.mli文件)来实现的,该接口将日期类型声明为 private:

(* Date.mli *)
type t = private {
  year: int;
  month: int;
  day: int;
}

(** Create a date object if and only if given acceptable values *)
val create : year:int -> month:int -> day:int -> t option

执行情况(Date.ml 文件)照旧。特别是,没有 private 类型定义中的关键字。

(* Date.ml *)
type t = {
  year: int;
  month: int;
  day: int;
}

let create ...

你也可以在这一点上有所创新。例如,你可以让库安全地公开由年月日派生的字段,如自年初以来的天数等。

© www.soinside.com 2019 - 2024. All rights reserved.