【问题标题】:Optimizing tail-calls in C#在 C# 中优化尾调用
【发布时间】:2010-09-10 00:10:48
【问题描述】:

我有一个深度递归函数,理论上即使输入很大,它也应该能很好地工作。问题是在撰写本文时我忘记了 C# 并没有很好地进行尾调用优化(如果有的话),所以对于任何足够复杂的输入,我都会得到 StackOverflowExceptions。该方法的基本结构是两个大方法,每个方法调用另一个。

public object Simplify(object param) {
    if (IsSimple(param))
        return param;
    else
        return Expand(param);
}

private object Expand(object param) {
    return Simplify(ProcessExpansion(param));
}

IsSimpleProcessExpansion 都有一个相对固定的堆栈深度——唯一的问题是 Simplify 和 Expand 之间的递归。您将如何减少此处的堆栈深度?

我正在考虑返回代表来计算结果,但这似乎有点矫枉过正 - 必须有一个更简单的方法。这是我对解决方案的想法(它不是很完善,因为我一直认为我一定是在这里做错了什么):

public object Simplify(object param) {
    object result = () => SimplifyInternal(param);
    while (result is Func<object>)
        result = ((Func<object>)result)();
    return result;
}

private object SimplifyInternal(object param) {
    if (IsSimple(param))
        return param;
    else
        return Expand(param);
}

private object Expand(object param) {
    return () => SimplifyInternal(ProcessExpansion(param));
}

所以我的两个问题是:

  1. 这个解决方案有什么可怕的错误?
  2. 你能想到更好的吗?

【问题讨论】:

  • 请注意,.NET 4.0 x64 运行时优化了尾调用,而 x86 则没有。完全愚蠢,我知道。
  • 为什么不在 F# 中编程?我宁愿让编译器优化它(语言功能),然后祈祷抖动会解决它。
  • 尽管我很喜欢 F#,但如果可以的话,我会将此评论排名。建议您将整个项目切换到 F# 只是因为您需要在某一时刻进行尾调用优化,这不是很有建设性。
  • @Brian True,您可以只将部分代码移植到 F#。尽管如此,我发现当你忙着尝试自己解决问题时,有人插入另一种语言有点笨拙——尤其是在这样做完全有可能和实际的情况下(有一个正式的证明您可以将所有递归转换为循环的地方,反之亦然)。
  • 好吧,OP 的问题最初是关于尾调用优化(尽管在 C# 中),但我不明白我们在这里使用的这种多语言/多范式框架的问题。东西总是在扩展/发展,知道你的选择永远不会对你产生负面影响......

标签: c# tail-recursion tail-call-optimization


【解决方案1】:

这不就是

while(!IsSimple(param))
    param = ProcessExpansion(param);
return param;

?

【讨论】:

  • 重点是,如果 A 只在尾部位置调用 B,而 B 只在尾部位置调用 A,那么你应该能够做一些按摩来合并这两个方法并包装一个 while 循环围绕它并获得相同的语义。
  • 是的。不过,这个例子被大大简化了——函数本身就足够复杂了,而且每个函数都做了完全不同的事情。 param 可以具有的不同类型的值还涉及一些多态性。理论上我可以将它们组合成一个函数,但这会使它们更难维护。
  • 确实如此。在这种情况下,一种可能的途径是在 F# 中编写这些函数。另一种是使用蹦床(看起来你正在尝试这个)。还有一个是使用线程(例如,当堆栈变高时,QueueUserWorkItem 并阻塞结果)。这些选择都不是非常有吸引力。使用您对创作和维护感到满意的那个。另外,请考虑您是否可以“改变问题”以首先解决问题。
  • +1 布赖恩是正确的。我要么在 F# 中编写函数,要么将其转换为循环,以更易于维护(记住你的同事和替代者以及你自己)。
猜你喜欢
  • 2011-03-31
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
  • 2014-04-06
  • 1970-01-01
  • 2012-08-19
  • 2011-05-27
  • 2019-04-20
相关资源
最近更新 更多