我写了两个版本的代码。第一个按预期工作并打印“嗨”。第二个给我一个错误,“阻止这个让未完成”
第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"
答案 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)
我认为理解这里的非光学语法很有帮助。让我们翻译:
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