3

我有一个 ocaml 类型:

type t = A | B |  ...

以及打印有关该类型的内容的功能:

let pp_t fmt x = match x with 
  | A -> Format.fprintf fmt "some nice explanations about A"
  | B -> Format.fprintf fmt "some nice explanations about B"
  | ...

我怎么能写一个函数来打印所有的解释?相当于:

let pp_all_t fmt = 
   Format.fprintf fmt A;
   Format.fprintf fmt B;
   ...

但是如果我忘记添加一个新的构造函数,那会警告我。拥有自动构建该功能的东西会更好,因为我的问题是它t很安静而且变化很大。

我无法想象如何在类型构造函数上“迭代”,但也许有一个技巧......

编辑:我最后做的是:

type t = A | B |  ... | Z
let first_t = A
let next_t = function A -> B | B -> C | ... | Z -> raise Not_found
let pp_all_t fmt = 
  let rec pp x = pp_t fmt x ; try let x = next_t x in pp x with Not_found -> ()
  in pp first_t

因此,当我更新时t,编译器会警告我必须更新pp_tandnext_t并且pp_all_t不必更改。感谢大家的建议。

4

3 回答 3

2

为了解决复杂且不断发展的类型的问题,实际上我可能会编写一个 OCaml 程序,该程序从包含值列表和相关信息的文件生成代码。

但是,如果您有一个incr_t : t -> t增加 type 值的函数t,并且让 t 的第一个和最后一个值保持不变,则可以编写以下代码:

let pp_all_t fmt =
    let rec loop v =
        pp_t fmt v;
        if v < Last_t then loop (incr_t v)
     in
         loop First_t

在 OCaml中不能有一个通用的多态incr_t,因为它只对构造函数为空值(不取值)的类型有意义。incr_t但是你可以为任何给定的类型编写自己的。

这种事情在 Haskell 中处理得非常好。基本上,当定义非常明显时,编译器会为您编写一些函数。OCaml 有一个类似的项目,称为deriving。我从未使用过它,但它似乎确实可以处理枚举值的问题。

既然你说你想要一个“技巧”,如果你不介意使用 OCaml 的不安全部分(我个人确实介意),你可以这样写incr_t

let incr_t (v: t) : t =
    (* Please don't use this trick in real code :-) !  See discussion below.
     *)
    if t < Last_t then
        Obj.magic (Obj.magic v + 1)
    else
        failwith "incr_t: argument out of range"

如果可能的话,我会尽量避免这种代码,这太危险了。例如,如果类型t获得了接受值的构造函数,它将产生无意义的值。真的是“等待发生的事故”。

于 2012-09-20T13:35:09.933 回答
1

人们需要某种形式的元编程来完成此类任务。例如,您可以探索从Jeffreyincr_t的答案中生成。

这是类似任务的示例代码:https ://stackoverflow.com/a/1781918/118799

于 2012-09-20T13:51:56.857 回答
1

您可以做的最简单的事情是定义所有构造函数的列表:

let constructors_t = [A; B; ...]

let pp_all_t = List.iter pp_t constructors_t

这是一个单行,简单的做法。诚然,它有点多余(灰色或黑暗魔法可以避免),但就“做我想做的事”/“有痛苦的副作用”比率而言,它仍然可能是最好的方法。

于 2012-09-20T14:24:31.617 回答