F#的函数式编程
函数式编程的核心是按照数学的方法来考虑我们的代码。看下面两个数学函数:这是两个数学函数,如果用F#代码实现写法不是这样子滴.
f(x) = x^2 + x
g(x) = x + 1
用参数2来套用:
f(2) = (2)^2 + (2)
g(2) = (2) + 1
当然也可以组合他们:
f g (2) = f(g(2))
= (g(2))^2 + (g(2))
= (2+1)^2 + (2+1)
= 12
你并不一定要像一个数学家来用F#编程,但你也以将数学家的想法用函数式程序来实现。例如,在以前的代码里我们没有定义一个明确的返回类型,那么f(x)是返回一个int还是一个float呢?下面两句F#代码具体有相同的类型:
1 let f x = x ** 2.0 + x
2 let g x = x + 1.0
以上两个函数类型都是float -> float
像这样的F#代码并不是说恰好两都是float,不是这样,函数式编程本质上是再一次抽象了计算方法,也就是考虑要计算什么,而不是如何计算。
我们甚至可以将整个程序做为一个有有一个输入输出点的函数来看待,当我们开始用这种方法看待程序时,复杂度将大大简化。
首先,如果我们将程序看成一连串的函数,那么我们就不需要把时间都花在解释程序是如何一步一步的完成一个任务这样的细节中去。函数只是简单的有进有出。按照函数式表达的算法不再有类或者对象,所以转换为函数式编程思想是非常容易的。
下面请先放下目前主流的编程语言体系吧!
不可变性
我们可能已经注意到,上面的一些代码我们并没有使用变量,而用于替代的是值(value)。这是因为在函数式编程里,你所定义的名称默认是固定不变的,也就意味着他们不能被改变。
如果一个函数因为某种原因改变了程序定义,比如说写文件操作,或者在内存中改变一个全局变量,这被认为是一个“副作用”。
副作用也并不全是坏处,但是意外的副作用是很多bug的根本。如果没有意识到函数里的副作用,那么就算是大多数优秀的程序员们也可能犯错。固定不变的值能帮助我们写出更安全的代码,因为我们无法把我们所不能改变的事情搞砸。也就是说值都已经固定了,又你又改变不了,想出问题都难,呵呵。
如果你习惯于命令式编程语言,那么不能拥有变量看上去非常别扭。但这种特性在函数式语言里也将提供一些非常多的好处。
看下面的例子:
1 let square x = x * x
2
3 //使用命令式风格
4 let imperativeSum numbers =
5 let mutable total = 0
6 for i in numbers do
7 let x = square i
8 total <- total + x
9 total
10
11 //使用函数式编程风格
12 let functionalSum numbers =
13 numbers
14 |> Seq.map square
15 |> Seq.sum
这两段代码返回的结果一样的,不过第一种是我们常见的命令式编程风格,看看是不是我们的函数式编程风格更具有优势呢?我们写代码大部分第一关注点是代码的可读性,通过良好的代码风格,我们可以很容易理解这段代码可以做什么事。函数式编程风格是注重描述的,或者也可以说是说明式的,这样的代码一般直观的告诉我们想要做什么。(例如上面的例子中,将每一个列表的元素先做开平方运算,再求和)。另外,如果想并行的运行命令式语言编写的代码,我们可能需要完全重写它。因为如果你想执行并行运算,你必须要详细的知道程序是如何运行的,也必须关心每一步的执行细节。但在函数式版本里,不用去关心如何做这些事情。在以后我们会介绍F#是如何简单的实现并行计算。
如果没有副作用的函数式编程语言我们称做纯函数式语言。在这一点上,F#算不上纯函数式语言,因为他允许我们在用命令式风格编写的时候可以改变变量的值。就像上面的第一个例子一样(用for循环那个)。
下一篇将会介绍 “函数的值”