Окамль - PullRequest
       34

Окамль

4 голосов
/ 04 декабря 2010

я создал тип t = Test of int * t ref

Как создать любой объект типа t?

1 Ответ

11 голосов
/ 04 декабря 2010

Как сказал Делнан, вы можете использовать циклические значения:

let rec x = Test (0, ref x)

Хотя рекурсия обычно используется для определения функций, она возможна для определенных значений. В общем, это невозможно, есть ограничения, описанные в документации .

Идея состоит в том, что это значение выделяется кучей, поэтому его легко использовать рекурсивно: сначала выделите место для конструктора "Test", затем вы можете определить его поле, используя для "x" адрес выделенного конструктор, даже если он указывает на еще не полностью определенное значение. Функции, ленивые значения, пары, записи и т. Д., Все следуют этой схеме. Конечно, спецификация не настолько низкоуровневая («выделение кучи» не определено в руководстве по OCaml), есть немного более строгая синтаксическая спецификация.

После того, как вы загрузили значение по рекурсии значения, вы можете создать более сложные значения:

let rec x = Test (0, ref x)
let y = Test (1, ref x)
let (Test (_, r)) = x in r := y

Вы также можете сделать это напрямую

let rec x = Test (0, ref y)
and y = Test (1, ref x)

Также возможно, хотя на самом деле не рекомендуется, использовать "фиктивное значение", полученное из Obj. Так реализован модуль Queue стандартной библиотеки.

Проблема с этим определением заключается в том, что значения являются циклическими. Это может быть проблематично, если вы планируете перебирать «хвост» таких значений. Если вы планируете использовать поле int для отслеживания «длины хвоста» структуры (0 означает, что ссылка является фиктивным указателем, которому не следует следовать), это именно то, как реализован модуль Queue.

Возможны и другие варианты. Например, вы можете использовать модель, в которой обнуляемость указателя хвоста делается явной, используя тип параметра:

type t' = Test of int * t' option ref

let x = Test (0, ref None)
let y = Test (0, ref (Some x))

let rec length (Test (_, tail)) =
  match !tail with
  | None -> 0
  | Some tl -> 1 + length tl 

В этом случае вам не нужна рекурсия для начальной загрузки вашего типа, достаточно 1026 *. Конечно, поскольку типы опций имеют косвенную направленность, это связано с умеренными затратами на производительность.

PS: обратите внимание, что для типов с одним конструктором, таких как этот, вам может быть лучше с типом записи:

type t = {
  field : int;
  tail : t ref;
 }
...