编程范式-浅谈

我们在了解编程范式之前需要知道什么是范式,除了编程范式外你可能还接触过数据库设计范式、科学范式等等概念。本文旨在让大家初步了解编程范式的相关概念。

范式

范式(paradigm)的概念和理论是美国著名科学哲学家托马斯·库恩(Thomas Kuhn) 提出并在《科学革命的结构》(The Structure of Scientific Revolutions)(1962)中系统阐述的。库恩认为范式是指“按既定的用法,范式就是一种公认的模型或模式”。“我采用这个术语是想说明,在科学实际活动中某些被公认的范例--包含定律、理论、应用以及一起设备统统在内的范例--为某种科学研究传统的出现提供了模型”。

范式的特点

  • 范式在一定程度内具有公认性
  • 范式是由一个基本定律、理论、应用以及相关的仪器设备等构成的一个整体,它的存在给科学家提供了一个研究纲领
  • 范式还为科学研究提供了可模仿的成功先例。

编程范式

简单来说编程范式就是一种编程风格。它不是指某种特定的语言,而是你编程的方式。无论我们使用哪种编程语言,它们在实现时都会遵循一些策略,这种策略就是一种范式。

都有哪些编程范式

  • 面向对象编程
  • 函数式编程
  • 过程式编程
  • 结构化编程
  • 指令式编程
  • 声明式编程

面向对象编程

描述面向对象编程的关键概念(来源:javatpoint.com)

特点
  1. 继承 使分层关系
  2. 多态
  3. 封装
设计原则
  • 开闭原则
  • 里氏替换原则
  • 迪米特原则(最少知道原则)
  • 单一职责原则
  • 接口分离原则
  • 依赖倒置原则
  • 组合/聚合复用原则

函数式编程

特点
  1. 纯函数
  • 函数始终返回相同的值(对于相同的输入),不管调用多少次
  • 自包含(不使用全局变量)
  • 它不应该修改程序的状态或引起副作用
  1. 递归 函数式语言中用递归来实现循环,因为它没有命令式语言的循环和跳转语句。

  2. 变量是不可变的

  3. 函数是”第一等公民“ 函数可以赋值给变量、可以作为传递参数或者可以作为函数的返回值

  4. 惰性计算

  5. 高阶函数 参数为函数或返回值为函数的函数。

# 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 代码
from functools import partial
 
def mod( n, m ):
  return n % m
 
# 将mod方法的参数n 固定为 100,并返回一个新的函数 
mod_by_100 = partial( mod, 100 )
 
print mod( 100, 7 )  # 2
print mod_by_100( 7 )  # 2
  1. 柯里化 把一个多参数的函数转化为单参数函数的方法
// 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. 闭包

    闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。

var factor = 3  
val multiplier = (i:Int) => i * factor  
  1. 函子 函子是一个特殊的容器,通过一个对象实现,该对象具有map方法,map方法可以运行一个函数,改变传递的值.
// 一个容器,包裹一个值
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等.

声明式编程

声明式编程是对与命令式编程不同的编程范型的一种合称。 它们建造计算机程序的结构和元素,表达计算的逻辑而不用描述它的控制流程。声明式编程是一个笼统的概念,除了一些特定的领域专属语言之外,一些更加知名的编程范型也被归类为其子范型。

结论

编程范式降低了程序的复杂性。每个程序员在实现他们的代码时都必须遵循范式。不过每个范式都有优点和缺点,所以你要先对自己的目标和实现过程中可能遇到的问题有所了解。

不要把编程范式和固定的语言做强制对应,因为有些语言能支持多种范式。