F# 类型定义内的递归
我在尝试在 F# 中实现自动微分时遇到一些问题。我认为问题在于评估不“懒惰”。
这是我的代码:
type Diff =
{d : double; df : Diff}
static member (+) (x : Diff, y : Diff) =
{d = x.d + y.d; df = x.df + y.df}
static member (-) (x : Diff, y : Diff) =
{d = x.d - y.d; df = x.df - y.df}
static member (*) (x : Diff, a : double) =
{d = x.d * a; df = x.df * a}
static member (*) (x : Diff, y : Diff) =
{d = x.d * y.d; df = (x.df * y) + (y.df * x)}
let rec dZero = {d = 0.0; df = dZero}
let dConst x = {d = x; df = dZero}
let dId x = {d = x; df = dConst 1.0}
let test = dId 5.0
let add (x:Diff) = (x+x).d
如果我尝试使用“add test”,则会出现堆栈溢出错误,我认为这归因于我的类型本身依赖于“+”的 (+) 定义。
有什么办法可以解决这个问题吗?任何帮助将不胜感激。
非常感谢,阿什
I am having some problems with trying to implement Automatic Differentiation in F#. I think the problem is down to the evaluation not being 'lazy'.
Here is my code:
type Diff =
{d : double; df : Diff}
static member (+) (x : Diff, y : Diff) =
{d = x.d + y.d; df = x.df + y.df}
static member (-) (x : Diff, y : Diff) =
{d = x.d - y.d; df = x.df - y.df}
static member (*) (x : Diff, a : double) =
{d = x.d * a; df = x.df * a}
static member (*) (x : Diff, y : Diff) =
{d = x.d * y.d; df = (x.df * y) + (y.df * x)}
let rec dZero = {d = 0.0; df = dZero}
let dConst x = {d = x; df = dZero}
let dId x = {d = x; df = dConst 1.0}
let test = dId 5.0
let add (x:Diff) = (x+x).d
If I try to use 'add test' I get a stack overflow error, which I think is down to the definition of (+) inside my type itself relying on '+'.
Is there any way I can fix this? Any help would be greatly appreciated.
Many thanks, Ash
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您所想,问题在于 F# 不使用延迟求值,并且您正在创建的数据结构是“无限的”(因为 dZero 递归地引用自身)。计算
+
时,运算符对df
值调用+
,然后对df
值调用+
code>df.df 值等等...纠正此问题的一种方法是使记录的
df
成员显式惰性:这将评估
df 仅在实际使用时才值,因此
+
操作将计算d
的值,并仅为df
提供一个惰性值(如果有人需要的话可以评估)。另一种替代方法是使 Diff 类型成为可区分联合,并将零表示为特殊值(而不是递归记录),除非您对其他内容使用递归引用,否则这种方法将有效。声明大致如下:
这将使实现时间更长一些,因为您需要检查所有基元操作中的
Zero
情况。在这种情况下,您只会创建有限的数据结构(并且运算符会急切地处理它们)。As you thought, the problem is that the F# doesn't use lazy evaluation and that the data structure you're creating is "infinite" (because
dZero
recursively references itself). When calculating the+
, the operator calls+
on thedf
values and that in turn invokes+
on thedf.df
values and so on...One way to correct this is to make the
df
member of the record explicitly lazy:This will evaluate the
df
value only when it is actually used, so the+
operation will calculate the value ofd
and only provide a lazy value fordf
(which can be evaluated if someone needs it).Another alternative would be to make the
Diff
type a discriminated union and represent zero as a special value (rather than as a recursive record), which would work unless you use recursive references for something else. The declaration would be roughly something like:This would make the implementation a bit longer, because you would need to check for the
Zero
case in all the primitive operations. In this case, you would only create finite data structures (and the operators would process them eagerly).