(OCaml) Странный синтаксис, используемый в queue.ml - оператор `<-`

Просматривая библиотеку 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 связывает значение, указанное вторым членом 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.

2 ответа

Решение

Смотрите мой ответ в списке Caml

Зачем задавать один и тот же вопрос дважды в разных местах? Это только приводит к дублированию усилий, а знающие люди тратят свое время, чтобы ответить вам. Если вы хотите сделать это, пожалуйста, по крайней мере, опубликуйте перекрестные ссылки (от вашего сообщения stackru в архив списка и наоборот [1]), чтобы люди могли проверить, что он еще не ответил в другом месте.

[1] да, вы можете иметь циклические перекрестные ссылки, так как сообщение stackru изменчиво!

Семантика изменяемых полей и ссылок сильно изменилась (навсегда) между Caml Light и Objective Caml. Помните, что этот код является специфическим для Caml Light - и если вы хотите изучать Caml, вам лучше использовать Objective Caml, реализация которого все еще поддерживается. В Objective Caml изменяются только поля записей. Ссылки являются производным понятием, тип 'a ref определяется как:

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

Вы изменяете изменяемое поле с помощью синтаксиса foo.bar <- baz (где "bar" - это поле записи, а foo и baz - любое выражение, foo - типа записи)

В Caml Light поля записи являются изменяемыми, но поля типа суммы (варианты) также изменяемы; изменяемые варианты полей, однако, здесь не используются. См. http://caml.inria.fr/pub/docs/manual-caml-light/node4.6.html для документации.

В Caml Light запись может возвращать изменяемое местоположение, похожее на lvalue в C-подобных языках. Например, с изменяемым вариантом

type foo = Foo of mutable int 

Вы можете написать:

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

"foo <- bar" используется здесь для присвоения значения "bar" lvalue "foo", связанному с изменяемым шаблоном. В вашем примере используются два изменяемых шаблона:

 | { tail = Cons(_, ref newtail) as oldtail } -> 
  • oldtail - изменчивый шаблон, обозначающий изменяемое поле "tail" записи
  • (ref newtail) - это особый синтаксис, шаблон ссылок. Он связывает изменяемый шаблон "newtail", соответствующий местоположению ссылки. Другими словами, в Caml Light вы можете написать оператор ":=" следующим образом:

    префикс let:= r v = соответствует r с | ref loc -> loc <- v

Надеюсь, это поможет.

,

Редактировать:

О странном сообщении об ошибке: я думаю, что внутренне, Caml Light поддерживает список "идентификаторов значений" в области видимости, которые происходят из шаблона, соответствующего изменяемому полю (запись или вариант). Когда они видят foo <- bar выражение, они смотрят в этой среде, чтобы найти соответствующее местоположение. Такая среда является локальной для выражения, она никогда не ускользает. В частности, на верхнем уровне он пуст, и ошибки говорят о том, что в области действия не существует "идентификатор значения" (изменяемый шаблон).

Есть еще одна вещь: пространство имен идентификаторов значений и обычные идентификаторы не различаются. Когда вы сопоставляете идентификатор значения, Caml Light добавляет в область действия идентификатор значения (изменяемый), а также соответствующий идентификатор с соответствующим значением r. Это может быть довольно запутанным, так как вы можете изменить местоположение, но значение не изменится:

#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.

В OCaml <- оператор изменяет изменяемые поля или переменные экземпляра объекта (ссылки изменяются с :=). Тем не менее, есть и другие вещи, такие как ref в вашем сопоставлении с образцом, которые мне незнакомы. Я думаю, что это сигнализирует Caml Light, чтобы соответствовать ячейке в качестве ссылки (аналогично lazy в сопоставлении с образцом в OCaml), в результате чего переменная, которая является жизнеспособной в качестве левой части <- для мутации. Передача переменной в функцию передает значение переменной, которое не является изменяемым, и, следовательно, функция не может изменить его.

Итак: похоже на соответствие нового хвоста как ref newtail налаживает newtail как подслащенное имя, такое, что оценивая newtail превращается в !newtail' (где newtail' какое-то внутреннее имя, представляющее саму ссылку) и newtail <- foo превращается в newtail' := foo,

Хотя я на самом деле не знаю Caml Light, и мне незнакомо это засахаривание, если оно вообще существует в OCaml (код, который вы предоставили, не компилируется в OCaml), но, похоже, это происходит со мной.

Другие вопросы по тегам