首页 > 代码库 > ?数学函数——在函数式编程背后的动力

?数学函数——在函数式编程背后的动力

函数式思维的动力来自数学。数学函数有很多特色——函数式语言试图模拟真实世界。

所以一开始,我们以一个加1函数开始:

Add1(x) = x+1

这意思是什么?好吧,看起来十分直白。它意味着有一个操作以一个数字开始,然后给它加1。

我们引入一些术语:

  • 可以被函数作为输入的值的集合叫做domain。这样,它可能是实数集合,为了简单,我们仅限于整数。

  • 可以被函数作为输出的值的集合叫做range(更科学的应该是叫作codomain的image)。还是仅限于整数。

  • 函数被称作映射domain到range。

image

在F#中,我们这样定义:

let add1 x = x + 1

如果你在F#交互窗口输入,你可以看到结果(函数的签名):

val add1 : int -> int

仔细看一下输出:

  • 大体意思是:函数add1将domain的整数映射到range中的整数

  • add1定义为val,value的简写。我们等一下再讨论value。

  • 箭头->用来展示domain和range。这里,domain是int类型,range是int类型。

注意类型不是特定的,F#编译器会猜测函数在使用int。

数学函数的关键属性

数学函数有很多属性和你在过程式编程用到的函数所不同。

  • 函数对于给的的输入值会给你相同的输出

  • 函数没有负面效应

这个数学提供了很多有力的好处,所以函数式编程语言在设计时也是试图加强这些属性。我们反过来挨个看它们。

函数对于给的的输入值会给你相同的输出

在命令式编程,我们认为函数做事情或者计算事情。数学函数没有做任何计算——它纯粹是将输入映射到输出。事实上,另一个方式思考定义一个函数是简单地作为所有映射的集合。例如,以一个非常生硬的方式这样定义add1函数(用C#):

int add1(int input)
{ 
   switch (input)
   {
   case 0: return 1;
   case 1: return 2;
   case 2: return 3;
   case 3: return 4;
   etc ad infinitum
   }
}

很明显,我们不能为每个可能的整数设置一个case,但是原理是一样的。你可以看到绝对没有一个计算,仅仅是一个遍历。

函数没有负面效应

在数学函数,输入和输出逻辑上是两个不同的东西,它们都是预先定义好的。函数没有改变输入和输出——它仅仅是从domain映射已经存在的输入值到range中的已经存在的输出值。

换句话说,函数没有可能在输入有任何的效应。函数没有实际计算或者操作什么,它仅仅是一个美化了的遍历。

值的不变性是微妙的但是十分重要。如果我在做计算,我不期望数字来改变它,当我将它们相加的时候。比如,如果我有:

x = 5
y = x+1

我不期望加1后,x被改变。我会期望得到一个不同的数字y,同时x没有被碰到。在数学的世界里,整数已经以一个无法改变的集合存在,add1函数仅仅定义了它们之间的关系。

pure函数的能力

这种函数有重复的结果,而且没有负面效应,我们称之为pure函数。你可以用它们做很多有意思的东西:

  • 它们是简单地并行的。我可以拿所有1到1000的整数,说,给定1000个不同的CPU,我可以使它们同时在对应的整数值来执行add1函数。这是安全的,因为它们之间没有必要有任何的交互。不需要锁,mutex,semaphore等。

  • 我可以懒惰地使用函数,只有当我需要输出地时候才执行它。我可以确定结果都会是一样的,不管我是现在执行还是稍后。

  • 我只需要为一个特定的输入执行一个函数,然后缓存结果,因为我知道了相同的输入总是对应相同的输出。

  • 如果我有很多pure函数,我可以以我喜欢的方式执行它们。再声明一遍,它不会改变最终结果。

你看到了如果我们可以创建函数式编程语言,我们可以立马得到很多厉害的技术。确实你可以用F#做这些事情。

数学函数的unhelpful属性

输入和输出不能变

理论上不变的值看起来是个好主意,但是如果你不能以传统方式给变量赋值你如何完成工作呢?

我确保你这不像你可能想的那样是个问题。当你看完这个系列,你就会看到实际中是如何解决的。

数学函数常常只有一个输入和输出

你从图表看到,数学函数常常只有一个输出。确实是这样,尽管当你第一次看到它们的时候没有那么明显。

看起来是个很大的烦恼。不用更多参数,如何能做有用的事情?

有一个方式来做,而且,F#中它是对你完全透明的。它叫做“currying”。

事实上,你会在后面发现,这两种“unhelpful”属性会证实是难以置信的有用——也是使得数学函数非常厉害的关键部分。

?数学函数——在函数式编程背后的动力