【问题标题】:F# mutual recursion between modulesF#模块之间的相互递归
【发布时间】:2018-10-12 05:48:18
【问题描述】:

对于 F# 中的递归,现有文档清楚地说明了在只有一个函数调用自身或一组物理上相邻的函数相互调用的特殊情况下如何执行此操作。

但是一般情况下,不同模块中的一组函数需要相互调用,你是怎么做的呢?

【问题讨论】:

  • 我不确定这是否可能。我知道由于各种类型检查编译问题,这在 O'caml 中是不可能的。
  • @LB40:这是不正确的。 OCaml 具有递归模块。例如,要定义一个联合类型,它的构造函数采用一组您正在定义的类型的值,您必须使用递归模块。

标签: f# recursion


【解决方案1】:

我认为在 F# 中没有办法实现这一点。通常可以以不需要的方式来构建应用程序,因此如果您描述了您的场景,您可能会得到一些有用的 cmets。

无论如何,有多种方法可以解决此问题 - 您可以声明一条记录或一个接口来保存您需要从模块中导出的功能。接口也允许您导出多态函数,因此它们可能是更好的选择:

// Before the declaration of modules
type Module1Funcs = 
  abstract Foo : int -> int
type Module2Funcs = 
  abstract Bar : int -> int 

然后,模块可以导出一个实现接口之一的值,需要另一个模块的函数可以将其作为参数(或者您可以将其存储在可变值中)。

module Module1 = 
  // Import functions from Module2 (needs to be initialized before using!)
  let mutable module2 = Unchecked.defaultof<Module2Funcs>

  // Sample function that references Module2
  let foo a = module2.Bar(a)

  // Export functions of the module
  let impl = 
    { new Module1Funcs with 
        member x.Foo(a) = foo a }

// Somewhere in the main function
Module1.module2 <- Module2.impl
Module2.module1 <- Module1.impl

初始化也可以使用反射自动完成,但这有点难看,但是如果你真的经常需要它,我可以想象为此开发一些可重用的库。

在许多情况下,这感觉有点难看,重组应用程序以避免递归引用是一种更好的方法(事实上,我发现面向对象编程中类之间的递归引用经常令人困惑)。但是,如果您真的需要这样的东西,那么使用接口/记录导出函数可能是唯一的选择。

【讨论】:

  • 确实,正如您所说,它并不经常需要——主要是我想确保它在出现时不会成为一个大人物。对于极少数情况,导出接口似乎是一种足够好的解决方法。
  • 我没有意识到情况有多糟糕,这个缺陷不仅适用于相互递归的函数,还适用于所有模块间的引用。很遗憾,因为它是一门漂亮的语言,而这正是我当前项目想要的。
  • @rwallace:很多人都同意这是一个限制(确实如此),但是我认为设计良好的函数式程序通常会避免这个问题。也许如果您描述了您的项目(以高层次的方式),有人可以提供有关设计的提示(建议如何避免遇到此问题)。
  • 睡过了,我认为你是对的,像我为我当前的项目(定理证明者)考虑的功能强大的设计应该能够避免它。对于具有重要的面向对象组件的设计来说,这将是一个更大的问题。我猜他们正在考虑在未来的版本中解决这个限制。
【解决方案2】:

不支持。一个证据是,在 Visual stuido 中,您需要为 F# 正确排序项目文件。

recursively 在两个不同的模块中调用两个函数真的很少见。

如果确实发生了这种情况,您最好将这两个函数的共同部分排除在外。

【讨论】:

    【解决方案3】:

    我不认为不同模块中的函数可以直接引用其他模块中的函数。行为如此紧密交织的函数是否需要位于单独的模块中?

    如果您需要将它们分开,一种可能的解决方法是使您的函数更高阶,以便它们采用代表递归调用的参数,以便您以后可以手动“打结”。

    【讨论】:

      【解决方案4】:

      如果您在谈论 C#,并且两个不同程序集中的方法需要相互递归调用,我会将它们都需要知道的类型签名提取到第三个共享程序集中。不过我不知道这些概念与 F# 的映射情况如何。

      【讨论】:

        【解决方案5】:

        这里的明确解决方案将使用模块签名。签名文件包含有关一组 F# 程序元素(例如类型、命名空间和模块)的公共签名的信息。 对于每个 F# 代码文件,您可以拥有一个签名文件,该文件与代码文件同名,但扩展名为 .fsi 而不是 .fs。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-04
          • 2021-03-26
          • 2011-03-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多