F#:可以引用自身的递归值 [英] F# : A recursive value that can reference itself

查看:74
本文介绍了F#:可以引用自身的递归值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个记录:

type node = {
               content : string;
               parent : node option;
               branch : string option;
               children : seq<node> option;
            }

我想以这种方式实例化:

Which I want to instantiate this way :

let rec treeHead = {
                       content = "Value"
                       parent = None;
                       branch = None;
                       children = tree (Some(treeHead)) rows;
                   };

哪里

let rec tree (parent:node option) (rows:seq<CsvRow>) :seq<node> option

是一个递归函数,它获取节点的子节点(以构造树). 因此,您可以看到对象treeHead需要通过tree函数进行调用.

Is a a recursive function that gets the children of a node (to construct a tree). So as you can see the object treeHead needs to call itself through the tree function.

我希望通过这种方式来避免将treeHead用作可变值,然后再更改其child属性.

I want to it this way to avoid using treeHead as a mutable value and change its children property after.

我的问题是treeHead正在引发错误和警告.错误提示:

My question is that the treeHead is raising an error and a warning. The error says :

值'treeHead'将作为其自身定义的一部分进行评估.

The value 'treeHead' will be evaluated as part of its own definition.

警告说:

对要定义的对象的此和其他递归引用 将在运行时通过 使用延迟参考.这是因为您正在定义一个或 更多的递归对象,而不是递归函数.这个警告 可以通过使用'#nowarn"40"'或'--nowarn:40'来取消.

This and other recursive references to the object(s) being defined will be checked for initialization-soundness at runtime through the use of a delayed reference. This is because you are defining one or more recursive objects, rather than recursive functions. This warning may be suppressed by using '#nowarn "40"' or '--nowarn:40'.

首先,我这样做是否正确(我是说,不考虑可变的选择)?以及我该如何纠正.

First, am I doing this the right way (I mean, not considering the mutable choice) ? And how should I correct this.

推荐答案

具有不可变数据结构的东西是,它们是不可变的. (<-某些Yoda级别的东西)

The thing with immutable data structures is, they're immutable. (<-- some Yoda-level stuff right there)

使用可变数据结构,您首先要为自己创建一个盒子,然后开始将东西放入盒子中.您输入的内容之一可能是对盒子本身的引用.既然您已经有一个盒子,那就可以了,您可以参考它.

With a mutable data structure, you first create yourself a box, and then start putting things into the box. One of the things you put in may be a reference to the box itself. Since you already have a box, that's fine, you can take its reference.

对于一个不变的数据结构,这是行不通的:您必须枚举在框之前进入框的所有内容!因此包装盒本身不可能是其中之一.这就是编译器在错误消息the value foo would be evaluated as part of its own definition中告诉您的内容.不能做运气不好.

With an immutable data structure, this doesn't work: you have to enumerate all things that go into the box before the box even exists! So the box itself can't be one of these things. That's what the compiler tells you in the error message: the value foo would be evaluated as part of its own definition. Can't do. Bad luck.

如果您只能以不立即评估框的方式包装对框的引用,而是将评估推迟到以后,直到框被完全构造好...我知道!放在一个封闭的地方!

If only you could wrap the reference to the box in such a way as to not evaluate it right away, but defer the evaluation till later, till the box is fully constructed... I know! Put it in a closure!

let getMe42 x = 42

type R1 = { r1: int }
let rec x1 = { r1 = getMe42 x1 }  // Doesn't work: the definition of `x1` references `x1` itself

type R2 = { r2: unit -> int }
let rec x2 = { r2 = fun() -> getMe42 x2 } // This works: the definition of `x2` only creates a _closure_ around `x2`, but doesn't reference it right away

好吧,如果我可以创建一个lambda,也许可以通过将lambda传递给另一个函数并立即调用它来欺骗编译器?

Ok, so if I can create a lambda, maybe I can cheat the compiler by passing the lambda to another function and calling it right away?

let getMe42 f = printfn "%O" <| f(); 42

type R = { r: int }
let rec x = { r = getMe42 (fun () -> x) }

> System.InvalidOperationException: ValueFactory attempted to access the Value property of this instance.

该死!这支部队很强大.即使编译器无法证明我在编译过程中很顽皮,它也会将初始化巧妙地包装在Lazy<T>中,因此在运行时会出现错误.

Damn! The Force is strong with this one. Even though the compiler can't prove that I'm being naughty during compilation, it cleverly wraps the initialization in Lazy<T>, so I get an error at runtime.

足够有趣的是,在非常简单的情况下,只需插入一个简单的自引用,而无需进行复杂的处理,就可以了:

Interestingly enough, in the very limited case of just inserting a straight-up self-reference, without complex processing, it works:

type R = { r: R }
let rec x = { r = x }  // Works just fine, not even a warning.

您甚至可以一直走到海龟:

You can even go all the way to the turtles:

type R = { r: R }
let rec x = { r = { r = { r = { r = x } } } }  // Still works!

这是一个非常特殊的情况,对于我的存在,我看不出任何理智的理由.之所以起作用,是因为F#将后备字段编译为internal而不是private,这使它在构造记录后可以对字段进行突变.推论是,这仅在定义了类型的同一程序集中有效.

This is a very special case, for whose existence I can see no sane rationale. It works, because F# compiles the backing field as internal, not private, which allows it to mutate the field after constructing the record. A corollary is, this will only work within the same assembly where the type is defined.

递归值是Dark Dark Magic的一项功能,它要求您将灵魂分成两部分才能使用.我相信正确的说法是 hackrux .

Recursive values are one of those Dark Magic features that require you to split your soul in two pieces to use. I believe the proper term is hackrux.

不要这样做.只是不,好吗?回到光明的一面,我们也有饼干.

Don't do this. Just don't, ok? Come back to the light side, we have cookies too.

这篇关于F#:可以引用自身的递归值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆