本文将描述函数式编程的基本理论, 从而让你理解什么是函数式编程, 同时也会展示 "函数式编程" 和 "面向对象" 这两种不同风格的编程范式之间的区别. 本文是函数式编程系列的入门篇, 后续文章陆续会发出.
什么是函数
函数式编程背后的理论依据是数学, 数学函数背后有一系列有意思的特点, 而函数式编程语言则试图模拟这些特性.
让我们先来看一个简单的数学函数:
y = x + 1
这个数学函数的意图是显而易见的, 给定一个变量 x 然后返回 x + 1, 上面的数学函数用 C# 来表示:
- public int Add1(int x)
- {
- return x + 1;
- }
数学函数跟我们用命令式语言编写的函数相比有两个明显的区别:
同一个输入在数学函数里总能得到相同的返回值
数学函数没有副作用
输入值和输出值是不变的(immutable)
这三个区别具体是什么意思呢?
同一个输入在数学函数里总能得到相同的返回值
在命令式语言 (imperative programming) 中, 我们总是在函数内部做一些计算和处理, 然后返回最终结果. 在数学函数中没有计算和处理, 你可以认为数学函数的返回值是对输入值的一一映射. 上面提到的数学函数极端定义如下:
- int Add1(int x)
- {
- switch (x)
- {
- case 0: return 1;
- case 1: return 2;
- case 2: return 3;
- case 3: return 4;
- //...
- }
- }
当然现实环境中我们不会写出这样的代码, 但是数学函数的思想是类似的, 里面没有对事情的处理逻辑.
关于命令式语言和声明式语言的区别看这里: 命令式语言和声明式语言的区别 https://stackoverflow.com/questions/1784664/what-is-the-difference-between-declarative-and-imperative-programming
数学函数没有副作用
数学函数所做的事就是根据每一个输入返回不同的输出, 整个过程没有对输入值做任何改变, 在返回输出的过程中也没有对其他的任何环境造成影响, 在函数式语言里这样的函数被称为纯函数.
输入值和输出值是不变的
immutable 使得代码更加具有预测性, 考虑下面的代码:
- public int Add1(int x)
- {
- x = 2;
- return x + 1;
- }
- public void InvokeAdd1()
- {
- var x = 10;
- var y = Add1(x);
- Console.WriteLine($"x={x}, y={y}");
- }
你试图在 Add1 函数中实现 y = x + 1, 但是由于失误将 x 的值改为了 2, 从而导致了一个 bug,immutable 则强制你无法修改 x 的值, 一旦初始化就不能再修改. 显然 C# 默认是 mutable 的, 但在常见的函数式语言中, 变量声明式 immutable 的, 比如在 F# 中将 x 初始化为 6 然后再修改为 7 会编译出错:
let x = 6 x <- 7
作为一个修炼命令式语言多年的开发者也许会内心充满疑问, 无法修改变量真的靠谱? 答案是肯定的, 在后面的文章中将会展示这种用法.
纯函数带来的好处
看似不起眼的三个特点实际上会带来非常强大的益处, 所以函数式编程语言则试图把这三个特性带到他们的设计当中.
得益于纯函数的这种特点, 对同一个输入无论你重复调用多次, 都会返回同样的结果, 并且没有副作用, 因此:
所有的调用可以使并行的. 加入你想用 1 到 10000 之间的数字去调用 Add1 函数, 你可以把这些计算过程分配到 10 个不同的 CPU 上并行执行, 由于所有的调用都是没有副作用的, 所以你不需要使用命令式语言中的锁机制就能完成任务;
懒加载成为现实, 你可以在真正需要结果的时候去执行计算, 因为你可以保证在任何时刻对同一个输入返回的结果总是相同的
你可以对函数的计算结果进行缓存, 因为相同的输入总能得到相同的结果, 所以可以轻而易举的对函数增减缓存功能
后续文章即将发出, 请想了解函数式编程的童鞋支持并关注.
来源: https://www.cnblogs.com/xiandnc/p/9256503.html