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

Просматривая библиотеку 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 ]

4 голосов
/ 20 января 2011

См. Мой ответ в списке камер

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

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

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

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

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

В 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"связан в изменчивом образце.В вашем примере используются два изменяемых шаблона: </p>

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

    let prefix: = rv = match r с |ref loc -> loc <- v </p>

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

.

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

О странном сообщении об ошибке: я думаю, что внутренне 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.
0 голосов
/ 20 января 2011

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

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

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

...