我是一个 ocaml 菜鸟。到目前为止,使用普通的旧 ref 到 int 或其他简单的内置类型对我来说在所有方面都符合预期。我在元组的上下文中使用了它们,其中引用是元组的成员。我可以更新 refs,取消引用它们等。
# let e = 1, ref 1;;
val e : int * int ref = (1, {contents = 1})
# snd e := 2;;
- : unit = ()
# e;;
- : int * int ref = (1, {contents = 2})
# !(snd e);;
- : int = 2
但是,一旦我向其他聚合(甚至是内置的简单)类型声明了一个命名类型“ref”,事情就会变得很糟糕。我发现我不能像以前那样更改引用,因为它们没有被声明为“引用”类型。这 !和 := 运算符失败。
并且语义似乎以奇怪的明显不一致的方式发生变化。下面只是一个例子。为什么在下面编写第一个代码块是合法的,但在顶部循环(下面进一步)做类似的事情似乎是非法的?第一个块被编译器接受,我们可以在其中匹配一个构造类型的 ref 并使用 ! 第 13 行和第 14 行的运算符。这都在循环队列的上下文中,并使用 #use 从顶部循环中的文件加载:
type 'a element = 'a * 'a pointer
and 'a pointer = Pointer of 'a element ref;;
let next (_,n) = n;;
type 'a queue = 'a element option ref;;
let create () = None;;
(*passes compiler and behaves well*)
let enqueue queue x =
match !queue with
None ->
let rec elem = (x, Pointer (ref elem)) in
queue := Some elem;
| Some (_, Pointer last_newest_next) -> (*Insert between newest and oldest*)
let oldest = !last_newest_next in
let elem = (x, Pointer (ref oldest)) in
last_newest_next := elem;
queue := Some elem;;
在顶部循环中,类似的努力(以及对此的变体)失败了,如下所示,我还使用函数分解了一个元组,然后尝试调用相同的运算符:
let rec elem = (1, Pointer (ref elem));;
let last = !(next elem);;
Characters 12-22:
let last = !(next elem);;
^^^^^^^^^^
Error: This expression has type int pointer
but an expression was expected of type 'a ref
是的,我正在使用 -rectypes 但我想在不使用递归缩写类型的情况下尝试一次,从那以后我就一直坚持下去。请注意以下在顶部循环中的工作,但我不确定它是否等效以及我真正想要的:
let last = next elem;;
val last : int pointer = (* ... *)
如果第 14 行的第一个代码块被更改为不使用 ! 运营商,它打破了。重写(如下)导致入队函数通过编译器但行为不端:
(*compiles but fails - que only ever holds one item*)
let enqueue queue x =
match !queue with
None ->
let rec elem = (x, Pointer (ref elem)) in
queue := Some elem;
| Some (_, Pointer last_newest_next) ->
let oldest = last_newest_next in
let elem = (x, Pointer oldest) in
last_newest_next := elem;
queue := Some elem;;
一定是没有!运算符(以及一些其他更改),倒数第二行实际上是使 elem 中的指针指向自身,而不是更新不同的指针(从匹配的分解元素内)以指向 elem 最初的预期。无论如何,我仍然不明白为什么在类型化 ref 的顶部循环元组分解和从 ml 文件中执行相同操作之间的语义似乎不一致……如果这甚至是所有这一切的原因。还是从模式匹配中分解与通过函数分解元组不同?!?
我使用了一个出列函数来测试上述函数的行为:
let dequeue queue =
match !queue with
None -> raise Not_found
| Some (_, Pointer oldest_ref) ->
let oldest = !oldest_ref in
let (x, Pointer next_ref) = oldest in
let next = !next_ref in
if next == oldest then
queue := None
else
oldest_ref := next;
x;;
我可以理解为什么我可能想避开函数式语言中的 ref 单元格,但我需要知道在必要时如何使用它们(不是双关语)。