如何在(功能)F# 中创建递归数据结构值?

发布于 2024-09-06 03:33:43 字数 473 浏览 8 评论 0原文

type: 的值如何

type Tree =
    | Node of int * Tree list

具有以函数方式生成的引用自身的值?

对于树的合适定义,结果值应等于以下 Python 代码中的 x:

x = Tree()
x.tlist = [x]

编辑:显然需要更多解释。我正在尝试学习 F# 和函数式编程,因此我选择实现 覆盖树以前用其他语言编程过。这里相关的是,每个级别的点都是下面级别的点的子集。该结构在概念上达到了无穷级。

在命令式语言中,节点有一个包含其自身的子节点列表。我知道这可以在 F# 中强制完成。不,考虑到覆盖树算法,它不会创建无限循环。

How can a value of type:

type Tree =
    | Node of int * Tree list

have a value that references itself generated in a functional way?

The resulting value should be equal to x in the following Python code, for a suitable definition of Tree:

x = Tree()
x.tlist = [x]

Edit: Obviously more explanation is necessary. I am trying to learn F# and functional programming, so I chose to implement the cover tree which I have programmed before in other languages. The relevant thing here is that the points of each level are a subset of those of the level below. The structure conceptually goes to level -infinity.

In imperative languages a node has a list of children which includes itself. I know that this can be done imperatively in F#. And no, it doesn't create an infinite loop given the cover tree algorithm.

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

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

发布评论

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

评论(2

诗笺 2024-09-13 03:33:43

Tomas 的回答提出了两种在 F# 中创建递归数据结构的可能方法。第三种可能性是利用记录字段支持直接递归的事实(当在定义记录的同一程序集中使用时)。例如,以下代码可以正常工作:

type 'a lst = Nil | NonEmpty of 'a nelst
and 'a nelst = { head : 'a; tail : 'a lst }

let rec infList = NonEmpty { head = 1; tail = infList }

使用此列表类型而不是内置列表类型,我们可以使您的代码正常工作:

type Tree = Node of int * Tree lst
let rec x = Node(1, NonEmpty { head = x; tail = Nil })

Tomas's answer suggests two possible ways to create recursive data structures in F#. A third possibility is to take advantage of the fact that record fields support direct recursion (when used in the same assembly that the record is defined in). For instance, the following code works without any problem:

type 'a lst = Nil | NonEmpty of 'a nelst
and 'a nelst = { head : 'a; tail : 'a lst }

let rec infList = NonEmpty { head = 1; tail = infList }

Using this list type instead of the built-in one, we can make your code work:

type Tree = Node of int * Tree lst
let rec x = Node(1, NonEmpty { head = x; tail = Nil })
ま柒月 2024-09-13 03:33:43

如果递归引用没有延迟(例如包装在函数或惰性值中),则不能直接执行此操作。我认为动机是没有办法“立即”通过立即引用来创造价值,因此从理论角度来看这会很尴尬。

但是,F# 支持递归值 - 如果递归引用被延迟,您可以使用这些值(F# 编译器随后将生成一些代码来初始化数据结构并填充递归引用)。最简单的方法是将引用包装在惰性值中(函数也可以工作):

type Tree = 
    | Node of int * Lazy<Tree list>

// Note you need 'let rec' here!        
let rec t = Node(0, lazy [t; t;])

另一种选择是使用突变来编写它。然后你还需要使你的数据结构可变。例如,您可以存储 ref 而不是 Tree

type Tree = 
    | Node of int * ref<Tree> list

// empty node that is used only for initializataion
let empty = Node(0, [])
// create two references that will be mutated after creation    
let a, b = ref empty, ref empty

// create a new node
let t = Node(0, [a; b])
// replace empty node with recursive reference
a := t; b := t

正如 James 提到的,如果您不允许这样做,您可以拥有一些不错的属性,例如任何遍历该数据结构的程序都将终止(因为数据结构是有限的并且不能递归)。因此,您需要对递归值更加小心:-)

You cannot do this directly if the recursive reference is not delayed (e.g. wrapped in a function or lazy value). I think the motivation is that there is no way to create the value with immediate references "at once", so this would be awkward from the theoretical point of view.

However, F# supports recursive values - you can use those if the recursive reference is delayed (the F# compiler will then generate some code that initializes the data structure and fills in the recursive references). The easiest way is to wrap the refernece inside a lazy value (function would work too):

type Tree = 
    | Node of int * Lazy<Tree list>

// Note you need 'let rec' here!        
let rec t = Node(0, lazy [t; t;])

Another option is to write this using mutation. Then you also need to make your data structure mutable. You can for example store ref<Tree> instead of Tree:

type Tree = 
    | Node of int * ref<Tree> list

// empty node that is used only for initializataion
let empty = Node(0, [])
// create two references that will be mutated after creation    
let a, b = ref empty, ref empty

// create a new node
let t = Node(0, [a; b])
// replace empty node with recursive reference
a := t; b := t

As James mentioned, if you're not allowed to do this, you can have some nice properties such as that any program that walks the data structure will terminate (because the data-structrue is limited and cannot be recursive). So, you'll need to be a bit more careful with recursive values :-)

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