F#中的函数..为什么不编译

时间:2011-01-13 05:44:48

标签: f#

我写了两个版本的代码。第一个按预期工作并打印“嗨”。第二个给我一个错误,“阻止这个让未完成”

第1版

#light 

let samplefn() =
            let z = 2
            let z = z * 2
            printfn "hi"

samplefn()

第二版

#light 

let samplefn() =
            let z = 2
            let z = z * 2           

samplefn()

唯一不同的是第二个版本中缺少printfn。我使用Visual Studio 2010作为我的IDE。我对F#很新,但这个错误对我来说似乎很奇怪。我想我错过了一些非常重要的概念。请解释一下。

编辑:如果我在函数之外执行此操作,即使使用第一个版本的代码,我也会收到错误。

#light
let z = 2
let z = z * 2
printfn "Error: Duplicate definition of value z"

3 个答案:

答案 0 :(得分:7)

let将值绑定到标签,否则不会做太多其他操作。您的函数包含两个绑定但不使用它们,因此您会收到错误。

以另一种方式来思考,F#中的所有函数都需要一个返回值,它是函数中最后执行的表达式的值。 let没有返回值,因此您的函数无效。要解决此问题,您可以添加返回值,例如:

let samplefn() =
    let z = 2
    let z = z * 2
    ()

定义了一个绝对不执行任何操作的函数(返回unit)。也许更好的例子是:

let samplefn() =
    let z = 2
    let z = z * 2
    z

将返回4(标签z的绑定值)。

答案 1 :(得分:5)

我认为理解这里的非光学语法很有帮助。让我们翻译:

第1版(让绑定表达式)

let samplefn() =
    let z = 2 in
    let z = z * 2 in
    printfn "hi";;

samplefn();;

这里要理解的重要一点是,所有非顶级let绑定实际上都是let <variable> = <expression1> in <expression2>形式的表达式,其中<variable>绑定到新<expression1>的结果范围<expression2><expression2>是整个表达式的返回值。轻量级语法让你相信这样的绑定是变量赋值/语句,实际上F#中的几乎所有内容都是表达式。

以下可能更清楚地说明了这一点:

let a = (let b = 3 in b + 2) //a is 5

第二版(顶级允许绑定)

let z = 2;;
let z = z * 2;;
printfn "Error: Duplicate definition of value z";;

顶级let-bindings以;;终止,表示可以将其视为语句的完成。顶级是一个范围,这里我们在同一范围内尝试绑定z两次会出错。使用示例1中的let绑定的表达式形式,我们为表达式链中的每个子范围重新绑定z。请注意,我们可以在顶层执行类似的操作:

let z = (let z = 2 in z * 2);;

答案 2 :(得分:4)

不在顶级的let(例如你的缩进的)必须有一个语句(实际上是一个表达式,如pst notes),在赋值后称为“body”。在第一个示例中,正文是printfn "hi",而第二个示例没有正文。这就是编译器所抱怨的。

请注意,在函数定义中,内部let表达式实际上创建了嵌套作用域。也就是说,let z = z * 2实际上会创建一个名为z的新值,并将外部z次2的值绑定到该值,然后在let的正文中使用它(在这种情况下是printfn)。嵌套的let将始终具有正文。它是嵌套,允许看似重复的定义。

由于最外层let不需要正文,编译器会认为您正在尝试在同一作用域中重新定义z,这是一个错误。您可以使用括号告诉编译器如何正确解释最后一个示例:

let z = 2
(let z = z * 2
printfn "z = %d" z)
printfn "z = %d" z

以上将打印

z = 4
z = 2