sp; ret.Add(item); }
声明式的写法: var ret = list.Where((x) => x % 2 == 0);
多核与并行
使用命令式编程语言写程序时,我们经常会编写如x = x + 1这样的语句,此时我们大量依赖的是可变状态,或者说是“变量”,它们的值可以随程序运行而改变。可变状态非常强大,但随之而来的便是被称为“副作用”的问题,例如一个无需参数的void方法,它会根据调用次数或是在哪个线程上进行调用对程序产生影响,它会改变程序内部的状态,从而影响之后的运行效果。而在函数式编程中则不会出现这个情况,因为所有的状态都是不可变的。事实上对函数式编程的讨论更像是数学、公式,而不是程序语句,如x = x + 1对于数学家来说,似乎只是个永不为真的表达式而已。
函数式编程十分容易并行,因为它在运行时不会修改任何状态,因此无论多少线程在运行时都可以观察到正确的结果。假如两个函数完全无关,那么它们是并行还是顺序地执行便没有什么区别。
函数式编程特性 与技术
函数是一等公民 闭包
高阶函数 惰性求值
递归 缓存技术
不可变状态 尾调用消除
柯里化 内存回收
C#函数式支持
Linq涉及的C#语言特性:隐式类型、匿名类型、初始化器、迭代器、委托、泛型、泛型委托、匿名方法、Lamada表达式。
函数对象必须是某种委托类型. 在C#中,我们可以定义强类型的委托类型或泛型的委托类型,委托可以代表跟这个委托类型有相同参数的方法(静态方法,类方法)的引用.
在使用LINQ的时候我们可以经常看到高阶函数。举个例子,如果你想将一个已有的序列使用一些函数转换为一个新的序列,你将使用类似LINQ的select函数(函数作为输入):
var squares = numbers.Select( num => num*num );
函数是一等公民
对象是面向对象的第一型,那么函数式编程也是一样,函数是函数式编程的第一型。
我们在函数式编程中努力用函数来表达所有的概念,完成所有的操作。
在面向对象编程中,我们把对象传来传去,那在函数式编程中,我们要做的是把函数传来传去。
函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。
函数可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。
高阶函数:能接收函数做参数的函数。
1:函数自身接受一个或多个函数作为输入参数;
2:函数自身能输出(返回)一个函数;
不可变状态
纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的;
函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,使得单元测试和调试都更容易。
递归
由于变量不可变,纯函数编程语言无法实现循环,这是因为For循环使用可变的状态作为计数器,而While循环或DoWhile循环需要可变的状态作为跳出循环的条件。因此在函数式语言里就只能使用递归来解决迭代问题,这使得函数式编程严重依赖递归。
递归定义的计算的Scala代码如下:
def fact(n: Int):Int= {
if(n == 0) return 1
n * fact(n-1)
}
C#代码
Public int Fact(int n)
{
int acc = 1;
for(int k = 1; k <= n; k++){
acc = acc * k;}
}
尾递归
如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾