青训营第④课(编程范式)

111 阅读8分钟

编程范式

范式

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

特点

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

编程的范式

范式导图.png

命令式:面向过程,面向对象

声明式:函数式,响应式

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

语言

C语言:“中级语言”过程语言代表

  • 可对立,字节,地址直接操作
  • 代码和数据分离倡导结构化编程
  • 功能齐全:数据类型和控制逻辑多样化
  • 可移植能力强

C++:面向对象语言代表

  • C with Classes
  • 继承
  • 权限控制
  • 虚函数
  • 多态

Lisp:函数式语言代表

  • 与机器无关
  • 列表:代码即数据
  • 闭包

JavaScript:基于原型和头等函数的多范式语言

  • 过程式
  • 面向对象
  • 函数式
  • 响应式*

编程语言.png

面向对象编程:

  • 封装
  • 继承
  • 多态
  • 依赖注入*

基本概念:

  1. 类:类是对象的类型模板;
  2. 实例:实例是根据类创建的对象;
  3. 面向对象的三个基本特征:封装、继承、多态
  • 封装:封装即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体。根据我的理解,其实就是把子类的属性以及公共的方法抽离出来作为公共方法放在父类中。

  • 继承:继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的行为。子类继承父类后,子类就会拥有父类的属性和方法,但是同时子类还可以声明自己的属性和方法,所以子类的功能会大于等于父类而不会小于父类。

  • 多态:多态按字面的意思就是“多种状态”,允许将子类类型的指针赋值给父类类型的指针。即同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。多态的表现方式有重写,重载和接口,原生 JS 能够实现的多态只有重写。

    • 重写:重写是子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。

设计原则

  • 开闭原则
  • 里氏替换原则
  • 迪米特原则(最少知道原则)
  • 单一职责原则
  • 接口分离原则
  • 依赖倒置原则
  • 组合/聚合复用原则

函数式编程

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

  2. 变量是不可变的

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

  4. 惰性计算

  5. 高阶函数

    参数为函数或返回值为函数的函数。

  6. 偏函数

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

  7. 柯里化

    把一个多参数的函数转化为单参数函数的方法

  8. 闭包

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

  9. 函子

    函子是一个特殊的容器,通过一个对象实现,该对象具有map方法,map方法可以运行一个函数,改变传递的值.

优点
  • 易理解:纯函数不改变状态,完全依赖输入,因此易于理解
  • 并发:由于纯函数避免更改变量或其他外部数据,因此并发更容易实现
  • 惰性计算:函数式编程鼓励惰性求职,仅在需要时在进行计算
  • 调试和测试更轻松:纯函数只接收一次参数并产生不可更改的输出。由于不可变且没有隐藏输出,测试和调试变得更加容易
缺点
  • 性能可能较差
  • 编码困难:虽然编写纯函数很容易,但将其与应用的其余部分组合可能非常困难

结构化编程

结构化编程采用子程序、块结构、for循环以及while循环等结构,来取代传统的 goto。希望借此来改善计算机程序的明晰性、质量以及开发时间,并且避免写出面条式代码。

过程式编程

过程式编程派生自指令式编程,主要要采取过程调用或函数调用的方式来进行流程控制。流程则由包涵一系列运算步骤的过程(Procedures),例程(routines),子程序(subroutines), 方法(methods),或函数(functions)来控制。在程序执行的任何一个时间点,都可以调用某个特定的程序。任何一个特定的程序,也能被任意一个程序或是它自己本身调用。

面向过程问题

  • 数组与算法关联弱
  • 不利于修改和扩充
  • 不利于代码重用

响应式编程

  • 异步/离散的函数式编程

    • 数据流

    • 操作符

      • 过滤
      • 合并
      • 转化
      • 高阶
  • 观察者模式

  • 迭代器模式

  • Promise/EventTarget 超集*

响应式编程的 ”compose“

观察者模式.png

  • 合并
  • 过滤
  • 转化
  • 异常处理
  • 多播

响应式编程的 ”Monad“

  • 去除嵌套的 Observable

指令式编程

是一种描述电脑所需作出的行为的编程范型。指令式编程语言有 Fortran、BASIC 和 C等.

声明式编程

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

语言运行

语言运行.png

HtmlParser 将会分为两个主要模块:

  • Lexer主要负责将字符串(模版语言)进行分词,转换为 tokens

    • Stream:记录 tokens,状态机状态
    • emit 函数:添加 token
    • tokenizer 函数:遍历字符串,启动状态机
    • start:入口状态机
      • "<" => found_paren_l
      • _ => start
    • found_paren_l:找到左尖括号 ("<")
      • LETTER => label (LETTER 是 A-Za-z0-9 的字符串)
      • “/” => label
    • found_paren_r: 找到右尖括号 (">")
      • "<" => found_paren_l
      • _ => text (解析文本)
    • label:解析 Tag 名
      • LETTER => label
      • ">" => found_paren_r
      • " " => try_enter_attribute
      • "/" => try_leave_attribuite
    • try_enter_attribute: 准备开始解析属性
      • “/” => try_leave_attribute (自闭合标签)
      • “>” => found_paren_r
      • " " => try_enter_attribute
      • LETTER => attribute_key (解析属性名)
    • attribute_key:解析属性名
      • LETTER => attribute_key
      • "=" => attribute_value (解析属性值)
    • attribute_value: 解析属性值
      • """ (双引号) => attribute_string_value(解析字符串属性)
    • attribute_string_value: 解析字符串属性
      • LETTER => attribute_string_value
      • """ (双引号) => try_leave_attribute
    • try_leave_attribute: 属性解析完毕
      • " " => try_enter_attribute
      • ">" => found_paren_r
    • text: 解析文本
      • "<" => found_paren_l
      • _ => text
  • Parser则对从 Lexer 得到的 tokens 进行处理,构造出 AST

    • Parser语法规则:

      • 推导式:表示非终结符到(非终结符或终结符)的关系
      • 终结符:构成句子的实际内容。(token)
      • 非终结符:符号或变量的有限集合。它们表示在句子中不同类型的短语或子句
    • Parser_LL/LR

      • LL:从左到右检查,从左到右构建语法树
      • LR:从左到右检查,从右到左构建语法树

总结

命令式、声明式、面向对象本质上并没有优劣之分,面向对象和命令式、声明式编程也不是完成独立、有严格的界限的,在抽象出各个独立的对象后,每个对象的具体行为实现还是有函数式和过程式完成。在实际应用中,由于需求往往是特殊的,所以还是要根据实际情况选择合适的范式。

附录

函数式编程

mostly-adequate.gitbook.io/mostly-adeq… ramdajs.com/ rxjs.dev/

创建DSLparser 工具

pegjs.org/ github.com/antlr/antlr… github.com/yiminghe/ki…