常见的编程范式有:函数式编程、程序编程、面向对象编程、指令式编程等。在面向对象编程的世界,程序是一系列相互做用(方法)的对象(Class Instances),而在函数式编程的世界,程序会是一个无状态的函数组合序列。不一样的编程语言也会提倡不一样的“编程范型”。一些语言是专门为某个特定的范型设计的,如Smalltalk和Java支持面向对象编程。而Haskell和Scheme则支持函数式编程。现代编程语言的发展趋势是支持多种范型,如 C#、Java 8+、Kotlin、 Scala、ES6+ 等等。正则表达式
与成百种编程语言相比,编程范式要少得多。多数范式之间仅相差一个或几个概念,好比函数编程范式,在加入了状态(state)以后就变成了面向对象编程范式。算法
虽然范式有不少种,可是能够简单分为三类:数据库
计算机的硬件负责运行使用命令式的风格来写的机器码。计算机硬件的工做方式基本上都是命令式的。大部分的编程语言都是基于命令式的。高级语言一般都支持四种基本的语句:express
(1)运算语句
通常来讲都表现了在存储器内的数据进行运算的行为,而后将结果存入存储器中以便往后使用。高阶命令式编程语言更能处理复杂的表达式,产生四则运算和函数计算的结合。编程
(2)循环语句
允许一些语句反复运行数次。循环可依据一个默认的数目来决定运行这些语句的次数;或反复运行它们,直至某些条件改变。设计模式
(3)条件分支
允许仅当某些条件成立时才运行某个区块。不然,这个区块中的语句会略去,而后按区块后的语句继续运行。数组
(4)无条件分支
允许运行顺序转移到程序的其余部分之中。包括跳跃(在不少语言中称为Goto)、副程序和Procedure等。网络
循环、条件分支和无条件分支都是控制流程。
早期的命令式编程语言,例如汇编,都是机器指令。虽然硬件的运行更容易,却阻碍了复杂程序的设计。session
怎样为一个模糊不清的问题找到一个最恰当的描述(问题描述)? 抽象(Abstraction)一般是咱们用来简化复杂的现实问题的方法。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。对象则指的是类的实例。它将对象做为程序的基本单元,将程序和数据封装其中,以提升软件的重用性、灵活性和扩展性,对象里的程序能够访问及常常修改对象相关连的数据。对象包含数据(字段、属性)与方法。数据结构
面向对象程序设计能够看做一种在程序中包含各类独立而又互相调用的对象的思想,这与传统的思想恰好相反:传统的程序设计主张将程序看做一系列函数的集合,或者直接就是一系列对计算机下达的指令。面向对象程序设计中的每个对象都应该可以接受数据、处理数据并将数据传达给其它对象,所以它们均可以被看做一个小型的“机器”,即对象。目前已经被证明的是,面向对象程序设计推广了程序的灵活性和可维护性,而且在大型项目设计中广为应用。此外,支持者声称面向对象程序设计要比以往的作法更加便于学习,由于它可以让人们更简单地设计并维护程序,使得程序更加便于分析、设计、理解。反对者在某些领域对此予以否定。
当咱们提到面向对象的时候,它不只指一种程序设计方法。它更多意义上是一种程序开发方式。在这一方面,咱们必须了解更多关于面向对象系统分析和面向对象设计(Object Oriented Design,简称OOD)方面的知识。许多流行的编程语言是面向对象的,它们的风格就是会透由对象来创出实例。重要的面向对象编程语言包含Common Lisp、Python、C++、Objective-C、Smalltalk、Delphi、Java、Swift、C#、Perl、Ruby 与 PHP等。面向对象编程中,一般利用继承父类,以实现代码重用和可扩展性。
一种编程范式,与命令式编程相对立。它描述目标的性质,让计算机明白目标,而非具体过程。声明式编程不用告诉计算机问题领域,从而避免随之而来的反作用。而命令式编程则须要用算法来明确的指出每一步该怎么作。声明式编程一般被看作是形式逻辑的理论,把计算看作推导。声明式编程大幅简化了并行计算的编写难度。
常见的声明式编程语言有:
数据库查询语言(SQL,XQuery)
正则表达式
逻辑编程
函数式编程
组态管理系统等。
声明式编程透过函数、推论规则或项重写(term-rewriting)规则,来描述变量之间的关系。它的语言运行器(编译器或解释器)采用了一个固定的算法,以从这些关系产生结果。不少文本标记语言例如HTML、MXML、XAML和XSLT每每是声明式的。函数式编程,特别是纯函数式编程,尝试最小化状态带来的反作用,所以被认为是声明式的。不过,大多数函数式编程语言,例如Scheme、Clojure、Haskell、OCaml、Standard ML和Unlambda,容许反作用的存在。
不一样的范式的出现,目的就是为了应对不一样的场景,但最终的目标都是提升生产力。
过程式编程和面向对象编程的区别并不在因而否使用函数或者类,也就是说用到类或对象的多是过程式编程,只用函数而没有类的也多是面向对象编程。那么他们的区别又在哪儿呢?
面向过程实际上是最为实际的一种思考方式,能够说面向过程是一种基础的方法,它考虑的是实际地实现。通常的面向过程是从上往下步步求精,因此面向过程最重要的是模块化的思想方法。当程序规模不是很大时,面向过程的方法还会体现出一种优点。由于程序的流程很清楚,按着模块与函数的方法能够很好的组织。
当谈论函数式编程,会提到很是多的“函数式”特性。提到不可变数据,第一类对象以及尾调用优化,这些是帮助函数式编程的语言特征。提到mapping(映射),reducing(概括),piplining(管道),recursing(递归),currying(科里化),以及高阶函数的使用,这些是用来写函数式代码的编程技术。提到并行,惰性计算以及肯定性,这些是有利于函数式编程的属性。
最主要的原则是避免反作用,它不会依赖也不会改变当前函数之外的数据。
声明式的函数,让开发者只须要表达 “想要作什么”,而不须要表达 “怎么去作”,这样就极大地简化了开发者的工做。至于具体 “怎么去作”,让专门的任务协调框架去实现,这个框架能够灵活地分配工做给不一样的核、不一样的计算机,而开发者没必要关心框架背后发生了什么。
并非使用类才是面向对象编程。若是你专一于状态改变和密封抽象,你就是在用面向对象编程。类只是帮助简化面向对象编程的工具,并非面向对象编程的要求或指示器。封装是一个过程,它分隔构成抽象的结构和行为的元素。封装的做用是分离抽象的概念接口及其实现。类只是帮助简化面向对象编程的工具,并非面向对象编程的要求或指示器。
随着系统愈来愈复杂,系统就会变得愈来愈容易崩溃,分而治之,解决复杂性的技巧。面对对象思想的产生是为了让你能更方便的理解代码。有了那些封装,多态,继承,能让你专一于部分功能,而不须要了解全局。
总结
命令式编程、面向对象编程、函数式编程,虽然受人追捧的时间点各不相同,可是本质上并无优劣之分。 面向对象和函数式、过程式编程也不是完成独立和有严格的界限,在抽象出各个独立的对象后,每一个对象的具体行为实现仍是有函数式和过程式完成。
(1)函数
函数式编程中的函数,这个术语不是指命令式编程中的函数(咱们能够认为C++程序中的函数本质是一段子程序Subroutine),而是指数学中的函数,即自变量的映射(一种东西和另外一种东西之间的对应关系)。也就是说,一个函数的值仅决定于函数参数的值,不依赖其余状态。
在函数式语言中,函数被称为一等函数(First-class function),与其余数据类型同样,做为一等公民,处于平等地位,能够在任何地方定义,在函数内或函数外;能够赋值给其余变量;能够做为参数,传入另外一个函数,或者做为别的函数的返回值。
(2)纯函数
纯函数是这样一种函数,即相同的输入,永远会获得相同的输出,并且没有任何可观察的反作用。不依赖外部状态,不改变外部状态。
好比Javascript里slice和splice,这两个函数的做用类似。 slice符合纯函数的定义是由于对相同的输入它保证能返回相同的输出。splice却会嚼烂调用它的那个数组,而后再吐出来;这就会产生可观察到的反作用,即这个数组永久地改变了。
var xs = [1,2,3,4,5]; // 纯的 xs.slice(0,3); //=> [1,2,3] xs.slice(0,3); //=> [1,2,3] xs.slice(0,3); //=> [1,2,3] // 不纯的 xs.splice(0,3); //=> [1,2,3] xs.splice(0,3); //=> [4,5] xs.splice(0,3); //=> []
(3)变量与表达式
纯函数式编程语言中的变量也不是命令式编程语言中的变量(存储状态的内存单元),而是数学代数中的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不容许像命令式编程语言中那样可以屡次给一个变量赋值。好比说在命令式编程语言咱们写x = x + 1。函数式语言中的条件语句,循环语句等也不是命令式编程语言中的控制语句,而是一种表达式。
“表达式”(expression)是一个单纯的运算过程,老是有返回值;
“语句”(statement)是执行某种操做(更多的是逻辑语句。),没有返回值。
函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,并且都有返回值。好比在Scala语言中,if else不是语句而是三元运算符,是有返回值的。严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其余命令式控制结构进行编程。 固然,不少所谓的函数式编程语言并无严格遵循这一类的准则,只有某些纯函数式编程语言,如Haskell等才是完彻底全地依照这种准则设计的。
(4)状态
首先要意识到,咱们的程序是拥有“状态”的。 想想咱们调试C++程序的时候,常常会在某处设置一个断点。程序执行断点就暂停了,也就是说程序停留在了某一个状态上。这个状态包括了当前定义的所有变量,以及一些当前系统的状态,好比打开的文件、网络的链接、申请的内存等等。具体保存的信息和语言有关系。好比使用过Matlab、R之类的科学计算语言的朋友会知道,在退出程序的时候它会让你选择是否要保存当前的session,若是保存了,下次打开时候它会从这个session开始继续执行,而不是清空一切重来。你以前定义了一个变量x = 1,如今这个x还在那里,值也没变。这个状态就是图灵机的纸带。有了状态,咱们的程序才能不断往前推动,一步步向目标靠拢的。函数式编程不同。函数式编程强调无状态,不是不保存状态,而是强调将状态锁定在函数的内部,不依赖于外部的任何状态。更准确一点,它是经过函数建立新的参数或返回值来保存程序的状态的。
状态彻底存在的栈上。
(5)函数柯里化
curry 的概念很简单:将一个低阶函数转换为高阶函数的过程就叫柯里化。
// 柯里化以前 function add(x, y) { return x + y; } add(1, 2) // 3 // 柯里化以后 function addX(y) { return function (x) { return x + y; }; } addX(2)(1) // 3
(6)函数组合(Pointfree:不使用所要处理的值,只合成运算过程)
为了解决函数嵌套过深,洋葱代码:h(g(f(x))),咱们须要用到“函数组合”,咱们一块儿来用柯里化来改他,让多个函数像拼积木同样。
const compose = (f, g) => (x => f(g(x))); var first = arr => arr[0]; var reverse = arr = arr.reverse(); var last = compose(first, reverse); last([1, 2, 3, 4, 5]); // 5
(7)声明式与命令式代码
在咱们平常业务开发中,写的代码绝大多数都为命令式代码;
咱们经过编写一条又一条指令去让计算机执行一些动做,这其中通常都会涉及到不少繁杂的细节。
而声明式就要优雅不少了,咱们经过写表达式的方式来声明咱们想干什么,而不是经过一步一步的指示。
//命令式 let CEOs = []; for (var i = 0; i < companies.length; i++) { CEOs.push(companies[i].CEO) } //声明式 let CEOs = companies.map(c => c.CEO);
(1)高阶函数
高阶函数就是参数为函数或返回值为函数的函数。有了高阶函数,就能够将复用的粒度下降到函数级别。相对于面向对象语言,高阶函数的复用粒度更低。高阶函数提供了一种函数级别上的依赖注入(或反转控制)机制,在上面的例子里,sum函数的逻辑依赖于注入进来的函数的逻辑。不少GoF设计模式均可以用高阶函数来实现,如Visitor,Strategy,Decorator等。好比Visitor模式就能够用集合类的map()或foreach()高阶函数来替代。
(2)闭包
闭包的基础是一等函数(First-class function)。闭包在形式上就是一个函数内部定义另外一个函数,函数的堆栈在在函数返回后并不释放,咱们也能够理解为这些函数堆栈并不在栈上分配而是在堆上分配。
(3)访问权限控制
js中的做用域
(4)延长变量生命周期
在面向对象语言里,函数内的变量都是在栈上分配的,函数调用完成后,栈销毁,变量的生命周期结束。而对象是在堆分配的,会常驻内存,除非被手动或自动回收掉。
(5)函子
Functor(函子)遵照一些特定规则的容器类型。任何具备map方法的数据结构,均可以看成函子的实现。
Functor 是一个对于函数调用的抽象,咱们赋予容器本身去调用函数的能力。把东西装进一个容器,只留出一个接口 map 给容器外的函数,map 一个函数时,咱们让容器本身来运行这个函数,这样容器就能够自由地选择什么时候何地如何操做这个函数。
因为命令式编程语言也能够经过相似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不变性带来的。
(1)引用透明(Referential transparency)
引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或”状态”,只依赖于输入的参数,任什么时候候只要参数相同,引用函数所获得的返回值老是相同的。其余类型的语言,函数的返回值每每与系统状态有关,不一样的状态之下,返回值是不同的。这就叫”引用不透明”,很不利于观察和理解程序的行为。
没有可变的状态,函数就是引用透明(Referential transparency)
(2)没有反作用(No Side Effect)。(Lodash.js)
反作用(side effect),指的是函数内部与外部互动(最典型的状况,就是修改全局变量的值),产生运算之外的其余结果。函数式编程强调没有”反作用”,意味着函数要保持独立,全部功能就是返回一个新的值,没有其余行为,尤为是不得修改外部变量的值。函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,这样写的代码容易进行推理,不容易出错。这使得单元测试和调试都更容易。
还有一个好处是,因为函数式语言是面向数学的抽象,更接近人的语言,而不是机器语言,代码会比较简洁,也更容易被理解。
(3)无锁并发
没有反作用使得函数式编程各个独立的部分的执行顺序能够随意打乱,(多个线程之间)不共享状态,不会形成资源争用(Race condition),也就不须要用锁来保护可变状态,也就不会出现死锁,这样能够更好地进行无锁(lock-free)的并发操做。尤为是在对称多处理器(SMP)架构下可以更好地利用多个处理器(核)提供的并行处理能力。
(4)惰性求值惰性求值(lazy evaluation,也称做call-by-need)是这样一种技术:是在将表达式赋值给变量(或称做绑定)时并不计算表达式的值,而在变量第一次被使用时才进行计算。这样就能够经过避免没必要要的求值提高性能。(简单例子&&)