对函数式编程简单理解

 

函数式编程是一种编程范式,和面向对象的编程方式一样,是一种编程思想。函数式编程现在相当的火爆,最近也在关注这方面的思想。

对于面向对象编程主要有三点特性:封装,多态,继承

封装就是把对象的属性和对象的行为封装到一个定义的类里面

多态就是同一个对象可以表现为多种具体的形式

继承就是子类可以继承父类的属性和行为

面向对象编程的思想是把所有的事物都当做对象来看待,任何事物皆对象。我们在学习面向对象的编程的时候,最喜欢举的例子就是形状->长方形->正方形 ,对于这种例子,面向对象的编程的确是非常合适的。对于大多数的自然界事物,都是可以抽象出来一个具体的对象,在具体化对象的属性和行为。这种编程思想和人类的认知具体事物的方式非常接近,所以面向对象的方法一直流行了20几年。

但是在某些情况下,并不是世界上所有的事物都适合用对象来表示,最常见的就是数学中的科学计算和逻辑运算

 

我们举一个简单的例子,我们定义一个函数 f(x) = x*x + 2*x - 100 ,我们需要对f(1)到f(n)的结果进行求和,看看如何实现这一段逻辑

用面向对象的方式实现

面向对象的方式需要抽象出来一个对象来表现现实世界的事物,在这里,我们定义个函数接口,来表现f(x)这个函数,通过实现这个接口,来实现具体的算法.

下面是function类的定义

public interface Function {
	public int call(int x);
}

在来定义具体的实现函数

public class ConcreteFunction implements Function {
	@Override
	public int call(int x) {
		return (x * x + 2 * x + 1);
	}
}

最后我们定义一个静态类,我们发现这个类基本上没有任何意义,只是为了进行一次计算行为,当然我们还是可以抽象出来这一次计算行为,但是在这个例子里面,我们简单一点,就是定义了一个静态类实现这个功能

public class SumUtils {

	public static final int sum(int n) {

		int temp = 0;

		for (int i = 0; i < n; i++) {
			temp = temp + new ConcreteFunction().call(i);
		}

		return temp;
	}

	public static void main(String[] args) {
		System.out.println(SumUtils.sum(10));
	}

}

最后打印出来结果是 385.

用面向对象构造出来这个过程,有两个地方比较丑陋,对于不同的f(x)我们需要定义出具体的实现类,每一个实现类代表一个具体的算法,用面向对象的观点来看,不同的逻辑函数是不同的对象,这些对象之间完全没有任何联系,而且这些对象没有任何属性,不存在封装的必要,同时也不会有被继承的特性。在计算我们想要的结果的时候,我们必须在定义无意义的对象去找出我们想要的结果,这纯粹是为了面向对象而定义对象,最后一个SumUtils类完全没有任何意义。

从上面这个例子来看,面向对象并不是任何时候都适用的。下面我们看看用函数式编程如何实现这个功能。这里我们选择scala语言来实现

def main(args: Array[String]) = {

    var f = { x: Int => x * x + 2 * x + 1 }; 
    var result = 0;

    for (val x <- 0 to 9) {
    	result = result + f(x);
    }
    
    println(result);

  }

 我们首先定义了一个f 函数,可以采用def f(x:Int) = {x*x + 2*x +  1} 的方式,也是采用代码块的方式,然后进行循环调用,计算出来结果。

从上面的代码行数来看,四行代码可以搞定。如果用一些高阶功能,代码可以做到更大皆简化和最大的扩展性。极端情况下一行代码就可以搞定。用函数来表示数学中的函数,比用对象表现数学中的函数更形象,更具有可读性。

 函数式编程的特点:函数是第一公民,无副作用,内部不存在状态,易于并发。

 

有时候在想,面向对象和函数式编程结合起来,能够极大的简化我们的编程工作,写出跟易于扩展的和可读的代码。在设计上,两者思想是想通的。在不同的领域里面,用不同的思想去设计程序,这个是需要我们分析的。

其实面向对象程序中的设计模式,很多和函数式编程里面的思想想通。我们常用的模板模式和回调模式和函数式编程里面的高阶函数设计思想类似,当设计能力达到一定层次之后,在去看函数式编程,就会对设计模式的理解更家深入。

 

正好这两天也看到一个词,编程极端主义——大概说的是,当某了解某一概念的时候,在任何情况下都套用这一概念。

编程极端主义 (跟极限编程没有关系)是一种接受某种理论、在所有事情上检验它、在所有地方运用它的行为。一通实验,尘埃落定后,人们通常会回想这次极端行为,认识到“不错,这很有趣,但很明显,在Y上使用X明显不合适。干这个事情我们需要使用合适的技术!”

编程极端主义能够让你在学习一门新的技术或者概念的时候更深入的了解其特性,适用性以及局限性。基本上,java语言的设计思想就是一个编程极端主义的典型用例,在java里面,基本上任何东西都是对象(除了基本的类型之外),在学习了java之后,对面向对象的方式编程会有非常深入的了解,但是思想也会被局限在其中,导致在任何时候都会用面向对象的方式去思考,而这其中有一些并不适合面向对象的设计,最典型的就是java里面的Math类。

在学习函数式编程,也可以按照这个思路去学习,忘掉面向对象的设计,在任何情况下考虑函数式思想。haskell就是一门纯粹的函数式编程。