Ocaml 自参考

发布于 2024-10-06 04:49:08 字数 63 浏览 5 评论 0原文

我已经创建了类型 t = Test of int * t ref

如何创建类型 t 的任何对象?

I've created type t = Test of int * t ref

How to create any object of type t?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

油焖大侠 2024-10-13 04:49:08

正如delnan所说,您可以使用循环值:

let rec x = Test (0, ref x)

虽然递归通常用于定义函数,但对于某些值是可能的。这通常是不可能的,文档中描述了一些限制< /a>.

这个想法是,这个值是堆分配的,所以很容易递归地使用它:首先为“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 

在这种情况下,您不需要递归来引导您的类型,None就足够了。当然,由于选项类型具有间接性,因此这会带来适度的性能成本。

PS:请注意,对于像这样的单构造函数类型,使用记录类型可能会更好:

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

As delnan said, you can use cyclic values :

let rec x = Test (0, ref x)

While recursion is generally used to define functions, it is possible for certain values. This is not possible in general, there are restrictions described in the documentation.

The idea is that this value is heap-allocated, so it is easy to use it recursively : allocate the space for a "Test" constructor first, then you can define its field using for "x" the adress of the allocated constructor, even if it points to a not-yet-fully-defined value. Functions, lazy values, pairs, records, etc., all follow this pattern. Of course, the specification is not so low-level ("heap-allocated" is not defined in the OCaml manual), there is a slightly more restrictive syntaxic specification.

Once you have bootstrapped a value by value recursion, you can build more elaborate values :

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

You can also do that directly

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

It is also possible, though really not recommended, to use a "dummy value" produced by Obj. This is how the Queue module of the standard library is implemented.

An issue with this definition is that the values are cyclic. It can be problematic if you plan to iterate on the "tail" of such values. If you plan to use the int field to keep track of the "tail length" of the structure (0 means the reference is a dummy pointer that shouldn't be followed), this is exactly how the Queue module is implemented.

There are other options possible. You may for example use a model where the nullability of the tail pointer is made explicit by using an option type :

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 

In that case, you don't need recursion to bootstrap your type, None is enough. Of course, as option types have an indirection, this comes at a moderate performance cost.

PS : note that for one-constructor types such as this one, you may be better with a record type:

type t = {
  field : int;
  tail : t ref;
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文