递归函数参数

时间:2016-04-02 15:53:57

标签: recursion f# functional-programming

我做了一个简单的递归函数,并期望它能够工作(但它没有):

open System
open System.Threading

let f =
    let r = Random()
    let rec d =
        printfn "%d" (r.Next())
        Thread.Sleep(1000)
        d
    d
f

在Intellisense的帮助下,我最终获得了以下工作功能(但不理解为什么以前的功能不起作用):

open System
open System.Threading

let f : unit =
    let r = Random()
    let rec d() =
        printfn "%d" (r.Next())
        Thread.Sleep(1000)
        d()
    d()
f

那么为什么我需要明确说明unit()

2 个答案:

答案 0 :(得分:3)

在第一个版本中,您声明了一个递归对象(let rec d)或一个值。你是说d对象是递归的,但对象是如何递归的?它如何称呼自己?当然,这没有意义。

在F#中使用递归对象是不可能的,这就是你的第一个版本不起作用的原因。

在第二个版本中,您声明了一个递归函数(let rec d())。添加(),您明确声明d是一个函数。 此外,您明确声明,unit,函数f(仅调用一次)不会返回任何内容,或者至少,您说f将返回值{一种非特定的类型。在F#中,即使是最简单的函数也必须始终返回一个值。

在您的情况下,F#将尝试推断f将返回的类型。因为没有特定的类型注释,并且f没有做某事(比如计算)会使用特定类型返回特定值,F#编译器会将一般返回类型分配给f,但是您的代码仍然不明确,您必须指定unit类型(F#函数可以返回的最简单类型)更具体。

值限制错误确实与F#强大的类型推断有关。有关此错误,请查看this interesting article

答案 1 :(得分:2)

在第一次尝试中,您不是定义函数,而是定义。值d是根据自身定义的 - 也就是说,为了了解d是什么,您需要先了解d是什么。难怪它不起作用!

为了使这一点更清楚,我会指出你的定义与此类似:

let x = x

您希望这可行吗?

在第二次尝试中,您为d提供了参数。它是使其成为函数而不是值的参数。比较:

let rec x() = x()

执行时仍然会导致堆栈溢出,但至少它会编译:它是一个无条件调用自身的函数。

您不必专门为其提供unit参数,任何参数都可以。你可以把它变成数字,字符串,甚至是通用类型。只是当你不关心它是什么时,unit是最简单的选择。

并且您实际上不需要使用类型注释f。这是一个无关紧要的步骤。

总之,我想指出即使在第二个代码块中,f仍然是,而不是函数。实际上,这意味着f中的代码只会在定义f时执行一次,而不是每次提到f作为其他表达式的一部分时,这显然都是你直觉所期望的。