乐趣区

编程范式与函数式编程

编程范式与函数式编程

一、编程范式的分类

范,模范、典范也。范式即模式、方法。常见的编程范式有:函数式编程、程序编程、面向对象编程、指令式编程等。在面向对象编程的世界,程序是一系列相互作用(方法)的对象(Class Instances),而在函数式编程的世界,程序会是一个无状态的函数组合序列。不同的编程语言也会提倡不同的“编程范型”。一些语言是专门为某个特定的范型设计的,如 Smalltalk 和 Java 支持面向对象编程。而 Haskell 和 Scheme 则支持函数式编程。现代编程语言的发展趋势是支持多种范型,如 C#、Java 8+、Kotlin、Scala、ES6+ 等等。

与成百种编程语言相比,编程范式要少得多。多数范式之间仅相差一个或几个概念,比如函数编程范式,在加入了状态(state)之后就变成了面向对象编程范式。

虽然范式有很多种,但是可以简单分为三类:

1. 命令式编程(Imperative programming)

计算机的硬件负责运行使用命令式的风格来写的机器码。计算机硬件的工作方式基本上都是命令式的。大部分的编程语言都是基于命令式的。高级语言通常都支持四种基本的语句:

(1)运算语句

一般来说都表现了在存储器内的数据进行运算的行为,然后将结果存入存储器中以便日后使用。高阶命令式编程语言更能处理复杂的表达式,产生四则运算和函数计算的结合。

(2)循环语句

容许一些语句反复运行数次。循环可依据一个默认的数目来决定运行这些语句的次数;或反复运行它们,直至某些条件改变。

(3)条件分支

容许仅当某些条件成立时才运行某个区块。否则,这个区块中的语句会略去,然后按区块后的语句继续运行。

(4)无条件分支

容许运行顺序转移到程序的其他部分之中。包括跳跃(在很多语言中称为 Goto)、副程序和 Procedure 等。

循环、条件分支和无条件分支都是控制流程。

早期的命令式编程语言,例如汇编,都是机器指令。虽然硬件的运行更容易,却阻碍了复杂程序的设计。

1954 年开始开发的 FORTRAN,是第一个编译型的编程语言,支持命名变量、复杂表达式、副程序和其他一些功能。后来的二十年中,大量的其他高级命令式编程语言被发明出来。

在 1980 年后,面向对象编程有迅速的发展;面向对象编程语言均有着命令式的风格,但引入了类和对象的核心概念,从此编程进入了 OOP 时代。

2. 面向对象编程(元编程)(Object-oriented programming,OOP)

怎样为一个模糊不清的问题找到一个最恰当的描述(问题描述)?抽象(Abstraction)通常是我们用来简化复杂的现实问题的方法。

在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。

对象包含数据(字段、属性)与方法。

面向对象程序设计可以看作一种在程序中包含各种独立而又互相调用的对象的思想,这与传统的思想刚好相反:传统的程序设计主张将程序看作一系列函数的集合,或者直接就是一系列对计算机下达的指令。面向对象程序设计中的每一个对象都应该能够接受数据、处理数据并将数据传达给其它对象,因此它们都可以被看作一个小型的“机器”,即对象。目前已经被证实的是,面向对象程序设计推广了程序的灵活性和可维护性,并且在大型项目设计中广为应用。此外,支持者声称面向对象程序设计要比以往的做法更加便于学习,因为它能够让人们更简单地设计并维护程序,使得程序更加便于分析、设计、理解。反对者在某些领域对此予以否认。

当我们提到面向对象的时候,它不仅指一种程序设计方法。它更多意义上是一种程序开发方式。在这一方面,我们必须了解更多关于面向对象系统分析和面向对象设计(Object Oriented Design,简称 OOD)方面的知识。许多流行的编程语言是面向对象的, 它们的风格就是会透由对象来创出实例。

重要的面向对象编程语言包含 Common Lisp、Python、C++、Objective-C、Smalltalk、Delphi、Java、Swift、C#、Perl、Ruby 与 PHP 等。

面向对象编程中,通常利用继承父类,以实现代码重用和可扩展性。

3. 声明式编程(Declarative programming)

一种编程范式,与命令式编程相对立。
它描述目标的性质,让计算机明白目标,而非具体过程。
声明式编程不用告诉计算机问题领域,从而避免随之而来的副作用。
而命令式编程则需要用算法来明确的指出每一步该怎么做。

声明式编程通常被看做是形式逻辑的理论,把计算看做推导。
声明式编程因大幅简化了并行计算的编写难度,自 2009 起备受关注。

常见的声明式编程语言有:

数据库查询语言(SQL,XQuery)
正则表达式
逻辑编程
函数式编程
组态管理系统等。

声明式编程透过函数、推论规则或项重写(term-rewriting)规则,来描述变量之间的关系。它的语言运行器(编译器或解释器)采用了一个固定的算法,以从这些关系产生结果。

很多文本标记语言例如 HTML、MXML、XAML 和 XSLT 往往是声明式的。函数式编程,特别是纯函数式编程,尝试最小化状态带来的副作用,因此被认为是声明式的。不过,大多数函数式编程语言,例如 Scheme、Clojure、Haskell、OCaml、Standard ML 和 Unlambda,允许副作用的存在。

二、3 种编程范式的特点

       1. 过程式编程的核心在于模块化,在实现过程中使用了状态,依赖了外部变量,导致很容易影响附近的代码,可读性较少,后期的维护成本也较高。
       2. 函数式编程的核心在于“避免副作用”,不改变也不依赖当前函数外的数据。结合不可变数据、函数是第一等公民等特性,使函数带有自描述性,可读性较高。
       3. 面向对象编程的核心在于抽象,提供清晰的对象边界。结合封装、集成、多态特性,降低了代码的耦合度,提升了系统的可维护性。

       不同的范式的出现,目的就是为了应对不同的场景,但最终的目标都是提高生产力。

1、过程式编程(Procedural)

过程式编程和面向对象编程的区别并不在于是否使用函数或者类,也就是说用到类或对象的可能是过程式编程,只用函数而没有类的也可能是面向对象编程。那么他们的区别又在哪儿呢?

面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实际地实现。一般的面向过程是从上往下步步求精,所以面向过程最重要的是模块化的思想方法。当程序规模不是很大时,面向过程的方法还会体现出一种优势。因为程序的流程很清楚,按着模块与函数的方法可以很好的组织。

2、函数式编程(Functional)

当谈论函数式编程,会提到非常多的“函数式”特性。提到不可变数据,第一类对象以及尾调用优化,这些是帮助函数式编程的语言特征。提到 mapping(映射),reducing(归纳),piplining(管道),recursing(递归),currying(科里化),以及高阶函数的使用,这些是用来写函数式代码的编程技术。提到并行,惰性计算以及确定性,这些是有利于函数式编程的属性。

最主要的原则是避免副作用,它不会依赖也不会改变当前函数以外的数据。

声明式的函数,让开发者只需要表达“想要做什么”,而不需要表达“怎么去做”,这样就极大地简化了开发者的工作。至于具体“怎么去做”,让专门的任务协调框架去实现,这个框架可以灵活地分配工作给不同的核、不同的计算机,而开发者不必关心框架背后发生了什么。

3、面向对象编程(Object-Oriented)

并不是使用类才是面向对象编程。如果你专注于状态改变和密封抽象,你就是在用面向对象编程。类只是帮助简化面向对象编程的工具,并不是面向对象编程的要求或指示器。封装是一个过程,它分隔构成抽象的结构和行为的元素。封装的作用是分离抽象的概念接口及其实现。类只是帮助简化面向对象编程的工具,并不是面向对象编程的要求或指示器。

随着系统越来越复杂,系统就会变得越来越容易崩溃,分而治之,解决复杂性的技巧。面对对象思想的产生是为了让你能更方便的理解代码。有了那些封装,多态,继承,能让你专注于部分功能,而不需要了解全局。

总结

命令式编程、面向对象编程、函数式编程,虽然受人追捧的时间点各不相同,但是本质上并没有优劣之分。面向对象和函数式、过程式编程也不是完成独立和有严格的界限,在抽象出各个独立的对象后,每个对象的具体行为实现还是有函数式和过程式完成。

现代的程序员应该很少有门派之见了,应该集百家之所长,学习其它范式 (语言) 的优秀设计理念,集成到自己的代码 (产品、语言) 中,提升工作效率。

退出移动版