我们在了解编程范式之前需要知道什么是范式,除了编程范式外你可能还接触过数据库设计范式、科学范式等等概念。本文旨在让大家初步了解编程范式的相关概念。
范式
范式(paradigm)的概念和理论是美国著名科学哲学家托马斯·库恩(Thomas Kuhn) 提出并在《科学革命的结构》(The Structure of Scientific Revolutions)(1962)中系统阐述的。库恩认为范式是指“按既定的用法,范式就是一种公认的模型或模式”。“我采用这个术语是想说明,在科学实际活动中某些被公认的范例–包含定律、理论、应用以及一起设备统统在内的范例–为某种科学研究传统的出现提供了模型”。
范式的特点
- 范式在一定程度内具有公认性
- 范式是由一个基本定律、理论、应用以及相关的仪器设备等构成的一个整体,它的存在给科学家提供了一个研究纲领
- 范式还为科学研究提供了可模仿的成功先例。
编程范式
简单来说编程范式就是一种编程风格。它不是指某种特定的语言,而是你编程的方式。无论我们使用哪种编程语言,它们在实现时都会遵循一些策略,这种策略就是一种范式。
都有哪些编程范式
- 面向对象编程
- 函数式编程
- 过程式编程
- 结构化编程
- 指令式编程
- 声明式编程
面向对象编程
描述面向对象编程的关键概念(来源:javatpoint.com)
特点
- 继承 使分层关系
- 多态
- 封装
设计原则
- 开闭原则
- 里氏替换原则
- 迪米特原则(最少知道原则)
- 单一职责原则
- 接口分离原则
- 依赖倒置原则
- 组合/聚合复用原则
函数式编程
特点
- 纯函数
- 函数始终返回相同的值(对于相同的输入),不管调用多少次
- 自包含(不使用全局变量)
- 它不应该修改程序的状态或引起副作用
递归
函数式语言中用递归来实现循环,因为它没有命令式语言的循环和跳转语句。变量是不可变的
函数是”第一等公民“
函数可以赋值给变量、可以作为传递参数或者可以作为函数的返回值惰性计算
高阶函数
参数为函数或返回值为函数的函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# scala 代码
val salaries = Seq(20000, 70000, 40000)
val doubleSalary = (x: Int) => x * 2
# map 属于高阶函数,接收一个函数作为入参
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
# 这是一个返回函数的高阶函数
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
val schema = if (ssl) "https://" else "http://"
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
}
val domainName = "www.example.com"
def getURL = urlBuilder(ssl=true, domainName)
val url = getURL("users", "id=1") // "https://www.example.com/users?id=1": String偏函数
偏函数是指固定部分已知参数,同时返回一个接受剩余参数的函数。目的一方面是为了减少重复传参数;另一方面是为了降低函数的通用性、提高函数的适用性。
1 | # python 代码 |
柯里化
把一个多参数的函数转化为单参数函数的方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// javascript 代码
// 原有函数
function plus(x, y){
return x + y
}
// 柯里化后的函数
function plus_currying(y){
return function(x){
return x+y
}
}
plus(1,2) // 结果:3
plus_currying(1)(2) // 结果:3闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
1
2var factor = 3
val multiplier = (i:Int) => i * factor函子
函子是一个特殊的容器,通过一个对象实现,该对象具有map方法,map方法可以运行一个函数,改变传递的值.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 一个容器,包裹一个值
class Container {
// of 静态方法,可以省略 new 关键字创建对象
static of(value) {
return new Container(value)
}
constructor(value) {
this._value = value
}
// map 方法,传入变形关系,将容器里的每一个值映射到另一个容器
map(fn) {
return Container.of(fn(this._value))
}
}
// 测试
Container.of(3)
.map(x => x + 2)
.map(x => x * x)
优点
- 易理解:纯函数不改变状态,完全依赖输入,因此易于理解
- 并发:由于纯函数避免更改变量或其他外部数据,因此并发更容易实现
- 惰性计算:函数式编程鼓励惰性求职,仅在需要时在进行计算
- 调试和测试更轻松:纯函数只接收一次参数并产生不可更改的输出。由于不可变且没有隐藏输出,测试和调试变得更加容易
缺点
- 性能可能较差
- 编码困难:虽然编写纯函数很容易,但将其与应用的其余部分组合可能非常困难
结构化编程
结构化编程采用子程序、块结构、for循环以及while循环等结构,来取代传统的 goto。希望借此来改善计算机程序的明晰性、质量以及开发时间,并且避免写出面条式代码。
过程式编程
过程式编程派生自指令式编程,主要要采取过程调用或函数调用的方式来进行流程控制。流程则由包涵一系列运算步骤的过程(Procedures),例程(routines),子程序(subroutines), 方法(methods),或函数(functions)来控制。在程序执行的任何一个时间点,都可以调用某个特定的程序。任何一个特定的程序,也能被任意一个程序或是它自己本身调用。
指令式编程
是一种描述电脑所需作出的行为的编程范型。指令式编程语言有 Fortran、BASIC 和 C等.
声明式编程
声明式编程是对与命令式编程不同的编程范型的一种合称。 它们建造计算机程序的结构和元素,表达计算的逻辑而不用描述它的控制流程。声明式编程是一个笼统的概念,除了一些特定的领域专属语言之外,一些更加知名的编程范型也被归类为其子范型。
结论
编程范式降低了程序的复杂性。每个程序员在实现他们的代码时都必须遵循范式。不过每个范式都有优点和缺点,所以你要先对自己的目标和实现过程中可能遇到的问题有所了解。
不要把编程范式和固定的语言做强制对应,因为有些语言能支持多种范式。