F#奇妙游(27):大大的小小let
小小的let
let
,三个字符,一开始就要用,学到computation expression,居然还会发现let
别有洞天,值得一探,真是奇妙!
最初刚开始学习F#时,对let
的理解是,定义一个变量,或者定义一个函数。但从语法的角度看,结合当时的学习内容,这个理解也是没有问题的。
let x = 10
let f x = x + x
printfn "%A" x
printfn "%A" (f 10)
在帮助文档中,"let
所声明的实体的作用域限制在包含当前代码的域,仅限于申明之后的部分。"这样来看,可以理解为let
在当前作用域例引入一个名称。F#是严格按照顺序的语言,所以let
的作用域就是从let
开始,到当前作用域结束。
问题从哪里开始出现的呢?
let与模式匹配
大概从模式匹配这里开始,let
的语法就变得复杂了。
let (x, y) = (10, 20)
let (x, y) = (10, 20) in x + y
帮助里也说,let
的语法是:
let identifier-or-pattern [ : type ] = expressionbody-expression
let identifier parameter-list [ : return type ] = expressionbody-expression
这是所谓绑定的语法,第一个是把值绑定到一个标识符或者模式,第二个是把值(函数)绑定到标识符。这在C/C++里面都不是什么问题,申明变量,开辟一个内存,把这个内存的引用放在标识符里,把值放在内存里,这样就完成了绑定。
但是在函数式编程中,这个语法的含义就不那么直接。特别是,这里还用到标识符或者模式匹配。
- 这个函数式编程实际上是多范式的!(没错)所以可以引用地址、绑定值也是没问题的!(可能是错的)
- 这里的
let
另有含义。
而在大多数编程语言中,都有一个很重要的概念:叫做作用域。这里到底是不是作用域的概念?如果是,那么let
就是定义一个全局变量,或者局部变量。
let的函数式理解
那么let本质上到底是什么呢?为什么要问这个问题?
在看到MSDN中有一段关于verbose syntax的页面,里面有一个关键词in
,在构成let
的语法中,in
是可选的。那么,in
到底是什么呢?
假设我们有以下一段代码,我们把in加上去后,看起来是这样的。
let x = 10 in
let y = 100 in
let f z = x + y + z in
f 1
加上in
之后,这段代码的含义就变得更加清晰了,可以称之为nested let
绑定。
从语义上来看,我们在printfn "%d" (f 1)
中绑定了f
,而f
的定义中,又绑定了x
和y
。这里的in
就是一个作用域的概念,f
的作用域是x
和y
的作用域的子集。
那么如果我们要追求纯函数,去掉in
,这段代码应该怎么写呢?当然是三层匿名函数,分别绑定x
、y
、z
。
let fx x =
let fy y =
let fz z = x + y + z
fz 1
fy 100
f1 10
或者,利用匿名函数,写成这样:
(fun x ->
(fun y ->
(fun z -> x + y + z) 1) 100) 10
或者改成管道操作符,就是这样的。
10 |> fun x ->
100 |> fun y ->
1
|> (fun z -> x + y + z)
这段代码唯一的难点就是|>
,其他的含义十分明白。刚好就是把let
的绑定替换成对应匿名函数的参数绑定。
let identifier-or-pattern [ : type ] = expressionbody-expression
变为
expressionbody-expression |> fun identifier-or-pattern [ : type ] ->
这个函数的函数体,实际上就是let
的in
之后的表达式,直到其作用域结束。
结论
- 函数式编程的学习中,因为语言的语法设计,会有很多不同的理解,这些理解都是正确的,但是有些理解更加贴近语言的设计者的意图。
let
的可以理解为是一个匿名函数调用,let
的作用域是这个函数的函数体。- 苟日新,日日新,学习每天都开心。