我正在编写一个OCaml模块,用于定义与CPU寄存器对应的类型。该模块的界面如下:
(*
* Defines a type which represents a R3000 register.
*)
type t =
| R0 (* Always 0 *)
| AT (* Assembler temporary *)
| V0 | V1 (* Subroutine return values *)
| A0 | A1 | A2 | A3 (* Subroutine arguments *)
| T0 | T1 | T2 | T3 | T4 | T5 | T6 | T7 (* Temporary registers *)
| S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 (* Register variables *)
| T8 | T9 (* Temporary registers *)
| K0 | K1 (* Reserved for kernels *)
| GP | SP | FP (* Global/Stack/Frame pointer *)
| RA (* Return address *)
(*
* Conversion from/to [|0, 31|].
*)
val of_int : int -> t
val to_int : t -> int
(*
* Conversion to string for display.
*)
val of_string : string -> t
val to_string : t -> string
但是,我希望实现快速而不是重复。例如,我可以像这样编写of_int函数:
let of_int = function
| 0 -> R0
| 1 -> AT
(* ... *)
但它会很糟糕且不可维护。我不想这样做,因为它与我的编程宗教相冲突。而且,我不仅要做一次这种脏代码,而且要做四种函数。
我发现的第一个解决方案是使用预处理器(Camlp4或cpp)来生成我想要的代码。我发现这有点矫枉过正,但是如果你不能帮我解决我的第二个想法,我会使用这种方法。
经过一番思考后,我想我可以这样做:type regdescr = {
reg : t ;
name : string ;
index : int
}
let regs =
let htbl = Hashtbl.create 32 in
let li = [ (* regdescr defs here *) ] in
List.iter (Hashtbl.add htbl) li ;
htbl
但是,在这种情况下,我必须选择我想要散列的字段。在这种情况下,是否存在另一种解决方案而不是使也许我不知道的数据结构能够散列三个字段并对其中的三个字段执行搜索。
很抱歉这个答案可能很简单的长问题:)。
答案 0 :(得分:4)
看起来非常适合deriving。
(*
* Defines a type which represents a R3000 register.
*)
type t =
| R0 (* Always 0 *)
| AT (* Assembler temporary *)
| V0 | V1 (* Subroutine return values *)
| A0 | A1 | A2 | A3 (* Subroutine arguments *)
| T0 | T1 | T2 | T3 | T4 | T5 | T6 | T7 (* Temporary registers *)
| S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 (* Register variables *)
| T8 | T9 (* Temporary registers *)
| K0 | K1 (* Reserved for kernels *)
| GP | SP | FP (* Global/Stack/Frame pointer *)
| RA (* Return address *)
deriving (Enum,Show)
let of_int x = Enum.to_enum<t>(x)
let to_int x = Enum.from_enum<t>(x)
let to_string x = Show.show<t>(x)
let pr = Printf.printf
let () =
pr "%i %i %i\n" (to_int R0) (to_int RA) (to_int T8);
pr "%s %s %s\n"
(to_string (of_int 0)) (to_string (of_int 31)) (to_string (of_int 24));
pr "%s %s %s\n"
(to_string (Enum.pred<t>(A1))) (to_string A1) (to_string (Enum.succ<t>(A1)));
()
输出:
0 31 24
R0 RA T8
A0 A1 A2
编译:
ocamlc -pp deriving -I ~/work/contrib/deriving/0.1.1-3.11.1-orig/lib deriving.cma q.ml -o q
答案 1 :(得分:2)
只有三个单独的哈希表?
答案 2 :(得分:1)
您没有使用散列表从寄存器的一个部分表示转到另一个,而是考虑强迫自己总是仅操作指向完整描述的指针,以便您可以访问您喜欢的任何方面(索引,字符串表示, ...)只有指针取消引用?
您可以将表示(您的类型regdescr
)用作注册表。
您需要多长时间对匹配类型寄存器的值进行模式匹配?
如果你从未这样做过,你甚至可以完全取消reg
字段。
module Register :
sig
type t = private { name : string ; index : int }
val r0 : t
val at : t
val equal : t -> t -> bool
val hash : t -> int
val compare : t -> t -> int
end =
struct
type t = { name : string ; index : int }
let r0 = { name = "R0" ; index = 0 }
let at = { name = "AT" ; index = 1 }
let equal r1 r2 = r1.index = r2.index
let hash r1 = Hashtbl.hash (r1.index)
let compare r1 r2 = Pervasives.compare r1.index r2.index
end
注意:通过使用文件register.ml和register.mli定义Register
模块,可以使整个事物更具可读性。
如果有时需要模式匹配,可以保留构造函数字段,以便可以编写漂亮的模式匹配:
match r.reg with
R0 -> ...
| AT -> ...
但强迫自己只编写接受(并传递其被调用者)完整Register.t
的函数。
编辑:对于索引,首先编写以下通用函数:
let all_registers = [ r0 ; at ]
let index projection =
let htbl = Hashtbl.create 32 in
let f r =
let key = projection r in
Hashtbl.add htbl key r
in
List.iter f all_registers ;
Hashtbl.find htbl
然后传递你需要的所有投影:
let of_int = index (fun r -> r.index)
let of_name = index (fun r -> r.name)