在浏览 Caml Light 库以获取编程示例时,我偶然发现了以下代码,取自 Caml Light queue.ml
文件:
type 'a queue_cell =
Nil
| Cons of 'a * 'a queue_cell ref
;;
type 'a t =
{ mutable head: 'a queue_cell;
mutable tail: 'a queue_cell }
;;
let add x = function
{ head = h; tail = Nil as t } -> (* if tail = Nil then head = Nil *)
let c = Cons(x, ref Nil) in
h <- c; t <- c
| { tail = Cons(_, ref newtail) as oldtail } ->
let c = Cons(x, ref Nil) in
newtail <- c; oldtail <- c
;;
FIFO 数据结构的这种实现让我感到困惑。我得到了一般的想法,保留指向结构中最后一个条目的指针,以便在最后追加是可能的。这对我来说很有意义。然而,让我感到困扰的是如何做到这一点的语法。
考虑以下:
| { tail = Cons(_, ref newtail) as oldtail } ->
let c = Cons(x, ref Nil) in
newtail <- c; oldtail <- c
我这里的类型有问题。根据类型定义,
newtail
应该是 'a queue cell
类型,因为它是在模式匹配中使用 Cons(_, ref newtail)
检索的:如果我理解正确,这将意味着 newtail
绑定(bind)了 tail
记录字段的第二个成员指向的 值 (最初是引用)。那么
newtail <- c
是什么意思呢?如果我尝试用 (fun x -> x <- c) newtail
替换此语句,我会得到 The identifier x is not mutable.
,而代码听起来与我的原始变体完全相似。将这几行重写为如下含义是否相同?
| { tail = Cons(_, newtail) as oldtail } ->
let c = Cons(x, ref Nil) in
newtail := c; oldtail <- c
将问题更进一步,以下代码实际上做了什么?
type t = Nil | Node of (t ref);;
type box = {mutable field: t};;
let poke = function
| {field = Node(ref n)} -> n <- Nil
| {field = Nil} -> ()
;;
let test = {field = Node(ref (Node(ref Nil)))};;
poke test;;
test;;
写的一样吗
{field = Node(n)} -> n := Nil
和
{field = Node(ref n)} -> n <- Nil
?
更奇怪的是:以下代码返回
The value identifier a is unbound.
let a = Nil;;
a <- Nil;; (* The value identifier a is unbound. *)
有人可以花时间为我澄清
<-
的使用吗?这里的各种例子让我很困惑......谢谢!
编辑:这最初发布到 Caml 邮件列表,但我认为该帖子没有发布,所以我将其发布在这里。看来帖子确实有效;抱歉:邮件列表答案的链接(其原作者也在这里发布)是 https://sympa-roc.inria.fr/wws/arc/caml-list/2011-01/msg00190.html 。
最佳答案
在 the caml list 上查看我的回答
为什么要在不同的地方两次问同样的问题?这只会导致重复工作,知识渊博的人会浪费时间来回答你。
如果您想这样做,请至少发布交叉引用(从您的 stackoverflow 帖子到列表存档,反之亦然[1]),以便人们可以检查它是否尚未在其他地方得到回答。
[1] 是的,你可以有循环交叉引用,因为 stackoverflow 帖子是可变的!
.
编辑:
关于奇怪的错误消息:我认为在内部,Caml Light 在范围内维护一个“值标识符”列表,这些列表来自与可变字段(记录或变体)匹配的模式。当他们看到 foo <- bar
表达式时,他们会在那个环境中寻找相应的位置。这种环境是表达式的局部环境,它永远不会逃脱。特别是在顶层它是空的,并且错误告诉您范围中不存在“值标识符”(可变模式)。
还有一件事:值标识符的命名空间和通常的标识符是不同的。当您匹配一个值标识符时,Caml Light 将值标识符(可变)以及具有匹配右值的相应标识符添加到范围中。这可能会非常令人困惑,因为您可能会改变位置,但值不会改变:
#match ref 1 with (ref x) -> (x <- 2; x);;
- : int = 1
#match ref 1 with (ref x) as a -> (x <- 2; !a);;
- : int = 2
(值)标识符将覆盖任何旧标识符(值标识符与否)
#let x = 1 in let (ref x) = ref 2 in x;;
- : int = 2
(如果你不知道,
let pattern = e1 in e2
等价于 match e1 with pattern -> e2
(类型系统除外))由于标识符和值标识符的语法类是相同的,非可变标识符也会影响值标识符,从而产生不同的错误:
#let (ref x) = ref 2 in let x = 1 in x <- 3;;
Toplevel input:
>let (ref x) = ref 2 in let x = 1 in x <- 3;;
> ^^^^^^
The identifier x is not mutable.
关于reference - (OCaml) queue.ml 中使用的奇怪语法—— `<-` 运算符,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4750087/