【问题标题】:Check if function is declared recursive检查函数是否声明为递归的
【发布时间】:2015-11-25 12:44:40
【问题描述】:

是否可以检查一个函数是否被声明为递归的,即使用 let rec?

我有一个 memoize 函数,但它不适用于任意递归函数。如果用户使用这样的函数调用它,我想给出一个错误。 (请随时告诉我这是否是处理 F# 错误的坏方法)

【问题讨论】:

  • 什么是“任意递归函数”?你能给出一些你所问的代码示例吗?

标签: f#


【解决方案1】:

F# 代码编译为 .NET 通用中间语言。 F# rec 关键字只是对 F# 编译器的提示,它使正在定义的标识符在函数范围内可用。所以我相信没有办法在运行时发现函数被声明为递归的。

但是,您可以使用 System.Diagnostic.StackTrace 类 (https://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace.aspx) 在运行时获取和分析堆栈帧。但是访问堆栈信息会对性能产生重大影响(我假设您的记忆功能是为了加快程序性能)。由于编译器可以进行优化,因此程序的调试和发布版本中的堆栈信息也可能不同。

【讨论】:

  • 对于尾递归函数,堆栈跟踪方法是否会失败(更糟糕的是 - 默认情况下仅在发布模式下失败)?除非尾递归在堆栈上留下足够的信息,你可以告诉你正在做递归?
  • 可能是的,而且正如我所说的堆栈跟踪方法会显着影响性能
  • 感谢您的解释。很高兴知道“rec”实际上只是一个编译器提示(尽管该信息可以存储在某个地方并使用反射找到)。 StackTrace 解决方案很有趣。我假设由于 F# 的非循环性质,如果函数在调用堆栈中出现两次,则它必须是递归的。不,我不会真的用它来优化 - 我主要只是感兴趣和好奇,并试图帮助自己更好地学习和理解语言:)
【解决方案2】:

我不认为它可以以一种明智和全面的方式来完成。

不过,您可能需要重新评估您的问题。我假设您的函数对递归函数“不起作用”,因为它只记录第一次调用,而所有递归调用都转到非记忆函数?根据您的情况,这可能不是一件坏事。

记忆化以记忆换取速度。在大型系统中,如果您只关心最终结果,您可能不想支付记忆中间结果的成本。所有这些字典都会随着时间的推移而增加。

但是,如果您特别关心递归函数的记忆,则可以以一种适合记忆的方式显式地构造它。见linkedthreads。

所以我的回答是 memoize 函数根本不应该关心递归。如果用户想要记忆一个递归函数,他必须理解所涉及的权衡,并以一种允许记忆中间调用的方式构造函数如果这是他需要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 2011-09-04
    • 2015-03-19
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多