在浏览 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
的类型应为“队列单元
”,因为它是在模式中使用 Cons(_, ref newtail)
检索的匹配:如果我理解正确的话,这意味着 newtail
绑定了 tail
记录字段的第二个成员(最初是一个参考)。
那么 newtail <- c
是什么意思?如果我尝试用 (fun x -> x <- c) newtail
替换此语句,我会得到 Theidentifier 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
?
更奇怪的是:以下代码返回 值标识符 a 未绑定。
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。
While browsing the Caml Light library for programming examples, I stumbled across the following code, taken from the Caml Light queue.ml
file:
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
;;
This implementation of FIFO data structures puzzles me. I get the general idea, to keep a pointer to the last entry in the structure, so that appending at the end is possible. This makes perfect sense to me. However, it's the syntax of how this is done that bugs me.
Consider the following:
| { tail = Cons(_, ref newtail) as oldtail } ->
let c = Cons(x, ref Nil) in
newtail <- c; oldtail <- c
I have a problem with types here. By the type definition, newtail
should be of type 'a queue cell
, since it's retrieved using Cons(_, ref newtail)
in the pattern matching: if I understand correctly, this would mean that newtail
binds the value pointed by the second member of the tail
record field (which originally is a reference).
So what does the newtail <- c
means? If I try to replace this statement by (fun x -> x <- c) newtail
, I get The identifier x is not mutable.
, whereas the code sounds perfectly similar to the original variant to me.
Would rewriting these few lines to read as follows mean the same?
| { tail = Cons(_, newtail) as oldtail } ->
let c = Cons(x, ref Nil) in
newtail := c; oldtail <- c
Taking the question one step further, what does the following code actually do?
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;;
Is it the same to write
{field = Node(n)} -> n := Nil
and
{field = Node(ref n)} -> n <- Nil
?
Even stranger: the following code returns The value identifier a is unbound.
let a = Nil;;
a <- Nil;; (* The value identifier a is unbound. *)
Could someone take the time to clarify the use of <-
for me? The various examples here are pretty puzzling to me...
Thanks!
EDIT: This was originally posted to the Caml Mailing list, but I thought the post didn't make it, so I posted it here. It appears that the posting did work; sorry for that: the link to the mailing list answer (which its original author also posted here), is https://sympa-roc.inria.fr/wws/arc/caml-list/2011-01/msg00190.html.
发布评论
评论(2)
请参阅我在 caml 列表 上的回答
为什么同一个问题要在不同的地方问两次?这只会导致重复工作,知识渊博的人会浪费时间来回答你。
如果您想这样做,请至少发布交叉引用(从您的 stackoverflow 帖子到列表存档,反之亦然[1]),以便人们可以检查该问题是否在其他地方尚未得到解答。
[1] 是的,您可以进行循环交叉引用,因为 stackoverflow 帖子是可变的!
。
编辑:
关于奇怪的错误消息:我认为在内部,Caml Light 在范围内维护一个“值标识符”列表,这些标识符来自与可变字段(记录或变体)匹配的模式。当他们看到
foo <- bar
表达式时,他们会在该环境中查找相应的位置。这样的环境对于表达式来说是局部的,它永远不会逃脱。特别是在顶层它是空的,并且错误告诉您范围内不存在“值标识符”(可变模式)。还有一件事:值标识符和普通标识符的命名空间并不不同。当您匹配值标识符时,Caml Light 会向范围添加值标识符(可变)以及具有匹配右值的相应标识符。这可能会非常令人困惑,因为您可能会改变位置,但值不会改变:(
值)标识符将遮盖任何旧标识符(值标识符或不)
(如果您不知道,
让pattern = e2 中的 e1 相当于
将 e1 与模式匹配 -> e2
(类型系统除外))由于标识符和值标识符的语法类相同,因此不可变标识符也会隐藏值标识符,从而产生不同的错误:
See my answer on the caml list
Why ask the same question twice in different places ? This only leads to a duplication of efforts, with knowledgeable people wasting their time to answer you.
If you want to do that, please at least post cross-references (from your stackoverflow post to the list archive, and vice versa[1]), so that people can check that it hasn't been answered yet in the other place.
[1] yes, you can have cyclic cross-references, as the stackoverflow post is mutable!
.
Edit:
About the strange error message: I think that internally, Caml Light maintain a list of "value identifiers" in the scope, which come from pattern matching a mutable field (record or variant). When they see a
foo <- bar
expression, they look in that environment to find the corresponding location. Such environment is local to the expression, it never escapes. In particular at toplevel it is empty, and the errors tells you that no "value identifier" (mutable pattern) exists in the scope.There is another thing: the namespace of value identifiers and usual identifiers are not distinct. When you match a value identifier, Caml Light adds to the scope the value identifier (mutable), but also the corresponding identifier with the matched rvalue. This can be quite confusing as you may mutate the location, but the value won't change :
The (value) identifier will shadow any older identifier (value identifier or not)
(If you didn't know,
let pattern = e1 in e2
is equivalent tomatch e1 with pattern -> e2
(except for the type system))As the syntaxic classes for identifiers and value identifiers are the same, a non-mutable identifier will also shadow a value identifier, giving birth to a different error:
在 OCaml 中,
<-
运算符会改变可变字段或对象实例变量(使用:=
改变引用)。然而,还有其他一些事情发生,比如我不熟悉的模式匹配中的 ref 。我认为它向 Caml Light 发出信号,将单元格作为参考进行匹配(类似于 OCaml 中模式匹配中的lazy
),从而产生一个可以作为<-
用于突变。将变量传递到函数中会传递变量的值,该变量的值不是可变的,因此函数不能改变它。所以:它看起来像匹配新的尾部,因为
ref newtail
将newtail
建立为糖化名称,这样评估newtail
就会转换为! newtail'
(其中newtail'
是代表引用本身的一些内部名称)和newtail <- foo
转换为newtail' := foo< /代码>。
不过,我实际上并不了解 Caml Light,而且我也不熟悉这种糖分,即使它存在于 OCaml 中(您提供的代码不能在 OCaml 中编译),但这就是我身上发生的事情。
In OCaml, the
<-
operator mutates mutable fields or object instance variables (references are mutated with:=
). However, there are other things going on like theref
in your pattern matching that are unfamiliar to me. I think that it is signalling to Caml Light to match the cell as a reference (analogous tolazy
in pattern matches in OCaml), resulting in a variable that is viable as the left-hand side of<-
for mutation. Passing the variable into the function passes the value of the variable, which is not mutable, and therefore the function cannot mutate it.So: it looks like matching the new tail as
ref newtail
establishesnewtail
as a sugared name such that evaluatingnewtail
is transformed to!newtail'
(wherenewtail'
is some internal name representing the reference itself) andnewtail <- foo
transforms tonewtail' := foo
.I don't actually know Caml Light, though, and I am unfamiliar with this sugaring if it even exists in OCaml (the code you provided does not compile in OCaml) but that's what it looks like is happening to me.