5

我通常对编写这样的代码不满意:

let load_record_field cursor gets geti gett a = function
  | 0x01 -> let c, s = gets () in (a.a_record_uuid <- s; `More_record c)
  | 0x02 -> let c, s = gets () in (a.a_group <- s; `More_record c)
  | 0x03 -> let c, s = gets () in (a.a_title <- s; `More_record c)
  | 0x04 -> let c, s = gets () in (a.a_username <- s; `More_record c)
  | 0x07 -> let c, t = gett () in (a.a_creation_time <- t; `More_record c)
  .
  .
  .
  | 0xFF -> `End_of_record cursor

我已经最小化了样板,但我想知道是否有任何 OCaml 魔法可以让我完全消除它。

4

3 回答 3

2

这很简单:只需使用闭包进行设置,然后编写一个函数来抽象出样板文件

let load_record_field cursor gets geti gett a x =
  let frob get set =
     let (c,s) = get () in
     set s; `More_record c
  in
  function
  | 0x01 -> frob gets (fun s -> a.a_record_uuid <- s)
  | 0x02 -> frob gets (fun s -> a.a_group <- s)
  | 0x03 -> frob gett (fun s -> a.a_title <- s)
  ...

等等。

如果您使用像 Jane Street 的 fieldslib 这样的宏包,您可以做得更好。这会生成一流的字段,以及自动生成的 setter 和 getter。这意味着您不必每次都手动构建闭包。

于 2010-02-27T13:50:39.030 回答
1

理论上你可以逃脱的最短时间是:

frobnicate (function 
| 0x01 -> gets , a_record_uuid 
| 0x02 -> gets , a_group 
  ...
)

当然,你会被 OCaml 挫败,因为 1°Objective Caml 中没有“指向成员的指针”构造,所以你必须编写fun a s -> a.a_record_uuid <- s而不是a_record_uuid(至少)和 2° 类型系统不完全支持存在量化,使得函数的返回类型不能是预期的:

exists 'a. int -> (unit -> record * 'a) * ('a -> record -> unit)

我想你可以通过在记录中设置值的命名函数来解决 1°,如果你碰巧经常这样做:

type complex = { re : int ; im : int }
let re r c = { c with re = r }
let im r c = { c with im = i }

我想这有点不正统,但它通常会在以后得到回报,因为我倾向于在大多数功能情况下使用它们。您可以以命令式风格创建等效项,或者您可以接受函数的开销(它只添加大约 20 个字符)。

As or 2°,可以通过在函数中隐藏存在量词来解决:

let t e read write = let c, x = read () in write x e ; `More_record c

这会让你去:

let t = t a in
match 
  | 0x01 -> t gets a_record_uuid 
  | 0x02 -> t gets a_title
  ...

如果 CamlP4 支持某种糖用于赋值函数,我不会感到惊讶。同时,如果你使用引用而不是可变字段,你可以缩短它(因为引用是一等值,字段不是):

let t read reference = let c, x = read () in reference := x ; `More_record c

match 
  | 0x01 -> t gets a.a_record_uuid
  ...
于 2010-02-26T15:35:54.800 回答
0

我通常对编写这样的代码不满意

如果你问我,这是一个好品味的标志:-)


我不知道魔法,但我认为最好的方法是拆分样板:

  1. 每个可变字段的一个样板设置器函数。可能在不同的情况下有用。

  2. 一种将整数代码映射到“如何处理该字段”的数据结构

您可以使用表格而不是函数来实现记录扫描仪。下面是一个暗示性的例子。gets和之间的区别在gett这里是一个真正的踢球者。在接下来的内容中,

  • sf代表“字符串字段”
  • tf代表“时间场”
  • eor代表“记录结束”

我已经编造tabulatelookup适合我的榜样;使用任何有效的数据结构。

let sf set a c =     let c, s = gets() in (set a s; `More_record c)
let tf set a c =     let c, s = gett() in (set a t; `More_record c)
let eor    a c =     `End_of_record c

let fields = tabulate
  [ 0x01, sf a_record_uuid
  ; 0x02, sf a_group
  ; ...
  ; 0x07, tf a_creation_time
  ; ...
  ]

let load_record_field cursor gets geti gett a code = lookup fields code cursor a
于 2010-02-27T05:05:28.903 回答