编程范式-浅谈

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

范式

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

范式的特点

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

编程范式

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

都有哪些编程范式

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

面向对象编程

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

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

函数式编程

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

  2. 变量是不可变的

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

  4. 惰性计算

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

    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
  6. 偏函数
    偏函数是指固定部分已知参数,同时返回一个接受剩余参数的函数。目的一方面是为了减少重复传参数;另一方面是为了降低函数的通用性、提高函数的适用性。

1
2
3
4
5
6
7
8
9
10
11
# 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. 柯里化
    把一个多参数的函数转化为单参数函数的方法

    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
  2. 闭包

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

    1
    2
    var factor = 3  
    val multiplier = (i:Int) => i * factor
  3. 函子
    函子是一个特殊的容器,通过一个对象实现,该对象具有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等.

声明式编程

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

结论

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

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