在浏览 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/

10-10 02:05