数学函数

数学函数

函数式编程背后的动力


 函数式编程背后的动力来源自数学,数学函数拥有很多不错的特色,函数化语言试图现实世界中模拟。

首先,我们以为一个数添加1的函数开始。

 

Add1(x) = x+1

 它是什么意思呢?它似乎很简单,这意味着有一个操作,为一个数字加1。

我们先解释一些术语

  •  可以作为函数的输入的值的集合被称为域(domain),在这个例中,它可以是一组真实的数字,现在为了更加简单,我们限制它只能是整数。
  • 函数可能的输出值的集合被称为范围(range)【严格来说,是图片中的取值范围】。在这个例子中,我们也限制它只能是整数。
  • 该函数被称为映射(map)域到范围。

 

在F#中定义是这样的:

let add1 x = x + 1

 如果你在F#中的即使窗口输入(不要忘记双分号) ,就会看到结果(函数的签名):

val add1 : int -> int

 我们仔细看一下输出:

  • 总的来说是:“这个add1函数映射整数(域 domain)到整数(范围 range)”
  • add1定义为“val”,"value"的简写,嗯。。。。它是什么意思呢?我们很快就会讨论到值。
  • 箭头符号“->”用来表示从(域 domain)到(范围 range)在这个例子中,域是 int类型,范围也是int类型。

注意类型没有被指定,但F #编译器猜测函数正在为整数服务。(这能改变吗?是的,因为我们会看到不久的)

数学函数的关键特性

数学函数中的一些特性与你之前在编程中的特性不一样。

  • 一个输入值函数总是会给出一个相同的输出值
  • 函数没有副作用

这些属性提供了一些非常强大的好处,因此函数式编程语言试图在他们的设计中执行这些属性。让我们轮流看他们的每一个。

一个输入值函数总是会给出一个相同的输出值

在命令式编程中,我们认为函数是在“do 做”或者“calculate 计算”。数学函数不做任何的计算,只是单纯的从输入映射到输出。事实上,另一种简单的定义函数是所有映射的集合。例如,我们可以在C#中简陋的定义这个“add1”函数:

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
   }
}

 

很显然,我们不能为每个可能值都添加一个分支,但是原则上是一样的。你可以看过绝对没有任何的计算,只是查找。

函数没有副作用

在数学函数中,输入和输入在逻辑上是两个不同的东西,它们都是预定义的。函数不能改变输入和输出-它只是从域中预存在的值映射到范围中一个预存在的值。

换句话说,计算这个函数不能对输入有任何的影响或者任何其他事情。记住,计算这个函数不是实际的计算或者操纵任何事,只是查找。

值的“不变性”很微妙但是很重要。如果我在做数学,我不希望在我加的时候改变我的数字,例如,如果我这样:

x = 5
y = x+1

 

我不希望当我给x加什么数的时候x改变。我希望返回一个y,x原封不动。在数学的世界中,整数作为一个不可改变的集合已经存在,“add1”函数简单的定义他们之间的关系。

纯函数的力量

重复结果而且无副作用的函数称为“纯函数”,我们可以用它们做一些有意思的事

  • 他们一般是并行的,我可以用从1到1000的整数,说,给我1000个不同的CPU,我可以同时在每个CPU上给对应的整数执行“add1”函数,安全方面,它们不需要相互作用。不需要锁,互斥锁,信号量等。
  • 我可以用一个延迟函数,只有当我需要输出的时候计算。不管我计算的早或者晚,我可以确定答案是一致的。
  • 我只需要计算一个函数一次特定的输入,而且我可以把它缓存起来,因为我知道同样的输入会给出同样的输出。
  • 如果我有大量的纯函数,我可以任意顺序的执行它们。下次,它们的结果也不会有不同。

所以你可以看到如果我们用编程语言构造一个纯函数,我们立即就会获取很多强大的技能。事实上,你可以做用F#做所有的事:

  • 你已经在“Why use F#?”系列中看到并行的例子了
  • 计算延迟函数在“optimization”系列中讨论
  • 缓存函数结果叫做“memoization 备忘”也将在“optimization”系列中讨论
  • 不关注计算顺序使并发编程更简单,而且当函数调整顺序或者重构都不会引入bug

数学函数"无益的"特性

当用在编程的时候,数学函数也有一些看起来不是很好的特性。

  • 输入和输出的值是不可变的
  • 函数总是存在一个输入的一个输出

这些特性也反映在函数式编程语言上,让我们一个个的看看:

输入和输出的值是不可变的

不可变的值在理论上不错的想法,但在实际上如果你不能以传统的方式分配给变量,你怎么能完成工作?

我可以向你保证,这不是像你想的那个问题。当你通过这一系列的学习,你会看到在实践中是如何工作的。

函数总是存在一个输入的一个输出

像你看到的图解那样,数学函数总是只有存在一个输入和一个输出。这对于函数式编程语言也是的,尽管当你第一次用它的时候可能还不明显。

这看起来像是一个大烦恼。没有函数有两个(或者更多)参数你怎么做有用的事情?

当然,它变化了,有一个方法可以解决它,而且在F#中是完全透明的。它叫做“柯里化”,会很快出现在接下来的文章中。

事实上,你晚些就会发现,这两个没有用的特性变得非常的有用,而且这也是使函数式编程很强大的关键。


翻译有误,请指正,谢谢!

原文地址:http://fsharpforfunandprofit.com/posts/mathematical-functions/

翻译目录传送门:http://www.cnblogs.com/JayWist/p/5837982.html

posted @ 2016-09-07 09:35  JayWist  阅读(339)  评论(0编辑  收藏  举报