3.1.1 值的声明和范围

3.1.1 值的声明和范围

 

    我们已经知道,可以使用 let 关键字声明不可变值。我们尚未谈论值的范围,容易用一个具体的示例来解释。清单 3.1 非常简单的,但我们认为你会同意这是令人惊异,在四行代码的后面到底隐藏了多少细微的差别。

 

Listing 3.1 The scope of a value (F#)

 

let number = 42
printfn "%d" number
let message = "Answer: " + number.ToString()
printfn "%s" message

 

    清单 3.1 是非常简单:它声明了两个值— —用第一个值计算出第二个——然后,将它们输出到控制台。对我们来说,重要的是值的范围——即值可以被访问的代码区域。如你所期望的,在第一行声明了值 number 以后,就可以访问它了,而值 message 只在最后一行可以访问。您可以看一下这个代码,并验证我们正在使用的值,就在它们的范围内,所以我们的代码是正确的。

    我们将使用此代码来演示另外一件事。清单 3.1 中的示例看上去很象 C# 代码,但重要的是要理解解 F# 处理代码的不同方式。在第 2 章(2.2.4 节)我们涉及过这个主题,我们尝试在 C# 中只使用表达式来编写代码。我们已经看到,如果想让每个有效 F# 代码成为表达式,值绑定必须特别处理。确实,如果你用 C# 写代码来做与清单 3.1 同样的事情,编译器会四个语句看作一个序列。让我们现在看看 F# 如何 理解这个代码的。为了演示,我们在语法上做了些小改动(参见清单 3.2)。

 

Listing 3.2 Example of let binding with explicit scopes (F#)

 

let number = 42 in
(
printfn "%d" number;
let message =
"Answer: " + number.ToString() in
(
printfn "%s" message
)
)

 

    清单 3.2 最明显的几个变化是布局,但也值得注意的是引入了关键字 in,出现在每个 let 绑定后。如果我们要关闭默认语法,即空白是有意义的[1],添加关键字 in 是必须的。另一个变化是,跟在let 绑定后的代码块括在括号中。这样做是可选的,但它是靠近 F# 编译器如何理解代码我们写的代码。有趣的是,清单 3.2 中的代码是仍然有效 F# 代码,与前面的代码具有相同的含义——有时,你可能希望代码更明确,使用关键字 in 和额外的括号来把相要处理的表达式括起来。

    清单 3.2 中更明显的变化,是 let 绑定把值赋给符号,指定可以在表达式内使用这个符号。第一个 let 绑定声明了符号 number 代表 42,跟在 in 关键字后面的表达式中,用括号括起来。整个 let 绑定被当作一个表达式,它返回内部的表达式的值。例如,定义值 message 的 let 绑定是一个表达式,返回 printfn 的结果。这个函数的返回类型 unit,因此,整个表达式的结果也是 unit。

 

    注意

 

    一行代码以关键字 let 开始、以 in 结束,单独还不能成为有效的表达式,因为它缺少 let 绑定的主体。必须要指定一些表达式作为主体。

 

    下一个有趣的事情是这个表达式排序。前一个表达式(printfn "%d" number;),后一个表达式(let message …),如你所见,我们在这两个表达式之间加了一个分号。分号是 F# 的排序运算符,它指定分号前面的表达式计算应先计算,后面的后计算。在这个示例中,这意味着前一个表达式在后一个表达式之前计算。在排序运算符前面的表达式也应该返回 unit,否则返回值将会丢失。

 

注意

 

    当使用轻量级语法时,不必要包含分号,可以改用一个换行符。编译器使用缩进代码来找出哪些行是表达式,并自动在行尾处插入分号。

 

    到目前为止,我们还是只看到普通绑定,声明普通的值,但是,同一个 let 绑定也能用于声明函数,和嵌套绑定。下一节将学习。

-----------------

    [1] 默认设置是有时称为轻量级语法(lightweight syntax)。F# 还支持 OCaml-compatible 的语法,它是更多的 schematic,我们将在这个示例中使用。本书的其余部分我们不会使用它,万一你想尝试使用此语法,通过将指令的 #light "off" 添加到F# 源文件的开头,就可以打开它。

你可能感兴趣的:(F#,职场,C#,休闲,函数编程)