reference - (OCaml)queue.ml 中使用了奇怪的语法—— `<-` 运算符

标签 reference ocaml records mutable

在浏览 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 帖子是可变的!

The semantics of mutable fields and references has changed a lot (for good) between Caml Light and Objective Caml. Beware that this code is Caml Light specific -- and if you want to learn Caml, you should rather be using Objective Caml, which is the implementation that is still maintained. In Objective Caml, only records fields are mutable. References are a derived concept, the type 'a ref is defined as :

type 'a ref = { mutable contents : 'a } 

You change a mutable field with the syntax foo.bar <- baz (where "bar" is a record field, and foo and baz are any expression, foo being of a record type)

In Caml Light, record fields are mutable, but sum type fields (variants) are mutable as well; mutable variant fields are however not used here. See http://caml.inria.fr/pub/docs/manual-caml-light/node4.6.html for documentation.

In Caml Light, a record may return a mutable location, akin to a lvalue in C-like languages. For example, with the mutable variant

type foo = Foo of mutable int 

you may write:

let set_foo (f : foo) (n : int) = 
  match f with 
  | Foo loc -> 
     loc <- n 

"foo <- bar" is used here to assign a value "bar" to a lvalue "foo" bound in a mutable pattern. In your example, two mutable patterns are used :

 | { tail = Cons(_, ref newtail) as oldtail } -> 
  • oldtail is a mutable pattern denoting the mutable "tail" field of the record
  • (ref newtail) is a specific syntax, a pattern on references. It binds a mutable pattern "newtail" corresponding to the location of the reference In other words, in Caml Light you can write the ":=" operator as such:

    let prefix := r v = match r with | ref loc -> loc <- v

Hope that helps.

.

编辑:

关于奇怪的错误消息:我认为在内部,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/

相关文章:

c++ - 返回 vector 堆栈引用时出现问题

syntax - 当我想引用一个枚举的变体时,是否有办法停止重复该枚举的名称? [复制]

c# - 将对象的引用传递给新线程

ocaml - 如何在 OCaml 中使用正常的模运算

OCaml 中的 Printf 参数

c++ - 这个结构是如何被引用的?什么是冒号(:) at the bottom?

ocaml - 如何在ocaml中舍入一个数字?

vba - Access VBA 在新窗体上打开所有记录,但显示指定记录

f# - F# Quotations 能否用于创建适用于任意 F# 记录类型的函数?

mysql - phpMyEdit 知道记录在那里 - 它们只是不显示