编程范式笔记 | 青训营笔记

103 阅读5分钟

编程范式

课程介绍

  • 课程背景

    • 前端的主要编程语言是javascript
    • javascript 融合了多种编程范式的语言,灵活性高
    • 前端开发者需要根据不同场景在不同编程范式间自如切换
    • 进一步创造领域特定语言抽象业务问题
  • 课程收益:

    • 了解不同编程范式的起源和适用场景
    • 掌握 JavaScript 在不同的编程范式特别是函数编程范式的使用
    • 掌握创建领域特定语言的相关工具和范式
  • 为什么需要编程语言

    • 机器语言

    • 汇编语言

    • 中级语言

      • c 语言、C++
    • 高级语言

      • Lisp:列表及数据
      • JavaScript:过程式,面向对象,函数式,响应式
  • 程序语言的特性

    • 是否允许副作用
    • 操作的执行顺序
    • 代码组织
    • 状态管理
    • 语法和词法
  • 编程范式

    • 命令式:

      • 面向过程:把操作用过程就行分组
      • 面向对象:根据状态和操作进行分组
    • 声明式:

      • 函数式:通过一系列函数组合来声明逻辑
      • 响应式:通过数据流和映射函数来实现结果
  • 过程式编程

    • 自顶向下

      • 程序

        • 模块1

          • 变量

            • 数据结构
          • 函数

            • 函数
            • 语句
        • 模块2

          • 变量

            • 数据结构
          • 函数

            • 函数
            • 语句
        • 。。。

    • 结构化编程

      • 子程序
      • 块结构(顺序结构,选择结构,循环结构)
    • js 中面向过程

      • 变量 ---数据
      • 函数--- 算法
    • 思考:

      • 面向过程式编程有什么缺点?

        • 数据与算法关联弱
        • 不利于修改和扩充
        • 不利于代码复用
      • 为什么后面会出现面向对象?

        • 封装-->关联数据与算法
        • 继承-- >无需复用的情况下进行功能扩充
        • 多态-- >不同的结构可以进行接口共享,进而达到函数复用
        • 依赖注入--> 去除代码耦合(声明一些依赖)
    • 面向对象编程的五大原则

      • 单一职责原则SRP
      • 开放封闭原则OCP
      • 里式替换替换原则LSP:
      • 依赖倒置原则DIP:
      • 接口分离原则
    • 面向对象有什么缺点?

      • 面向对象编程语言的问题在于,它总是附带着所有它需要的隐含环境。你想要一个香蕉,但得到的却是一个大猩猩拿着香蕉,而且还有整个丛林。
  • 函数式编程范式

    • 函数是一等公民

      • //  聚合转发
        const BlogController = {
            index(posts) { 
                return Views.index(posts); 
            },
            show(post) { 
                return Views.show(post);
            },
            create(attrs) { 
                return Db.create(attrs); 
            },
            update(post, attrs) { 
                return Db.update(post, attrs); 
            },
            destroy(post) { 
                return Db.destroy(post); 
            }
        };
        
    • 纯函数、无副作用(优势:可缓存,可移植,可测试,可推理,可并行)

      const retireAge = 60;
      ​
      function retirePerson(p) {
          if (p.age > retireAge) {
          p.status = 'retired';
          }
      }
      ​
      //  上面的代码优化如下
      ​
      function retirePerson(p) {
          const retireAge=60;
          if (p.age > retireAge) {
              return {
                  ...p,
                  status: 'retired',
              };
          }
          return p;
      }
      
    • 高阶函数、闭包

      // 函数颗粒化
      function add(a,b,c) {
          return a + b + c
      }
      add(1,2,5)
      add(1,2,4)
      add(1,2,7)
      ​
      // 上面代码优化如下:
      ​
      const add = curry(add);
      const add12 = add_(1,2);
      ​
      add12(5);
      add12(4);
      add12(7);
      ​
      const add1 = add_(1);
      ​
      add1(2,5);
      add1(3,5);
      ​
      // 柯理化函数
      function curry(fn) {
          const arity = fn.length;
          return function $curry(...args) {
              if(args.length < arity) {
                  return $curry.bind(null,...args);
              }       
              return fn.call(null,...args);
          }
      }
      
const toUpperCase = x => x.toUpperCase();
const log = x =>console.log;
​
function alertUppercase(str) {
    log(toUpperCase(x))
}
const alertUppercaseFn = compose( log, toUpperCase);
​
// 高级函数,实现这些函数的一连串调用
const compose = (...fns) => (...args) => fns.reduceRight( (res,fn) => [fn.call(null,...res)],args)[0];

associativity: compose(f,compose(g,h))===compose(compose(f,g),h); map's composition law: compose(map(f),map(g))===map(compose(f,g))

函数式编程特性--Functor

  • 可以当做容器的类型,类型支持对容器内元素进行操作

    • 常见的 functor:Array(lterable)map, Promise.then
a.b != null ? (a.b.c != null ? (a.b.c.d !== null ? a.b.c.d.e: null) : null): null// 上面代码优化如下:
class Maybe {
    constructor(x) {
        this.$value = x;
    }
    map(fn) {
        return this.$value == null ? this:new Maybe(fn(this.$value));
    }
}
new Maybe(a).map(prop('b')).map(prop('c')).map(prop('d')).map(prop('e'))

函数式编程特性-- Monad

  • 可以去除嵌套容器的容器类型

  • 常见monad: ArrayflatMap Promisethen

    [1,2].flatMap( ()=> ([1,2]))
    ​
    Promise.resolve(1).then( r => Promise.resolve(2 * r))
    ​
    Maybe.prototype.flat =function (level = 1) {
        if(this.$value?.constructor !== Maybe) {
        return new Maybe(this.$value);
        return level ? this.$value.flat(--level): this.$value;
    }
    

函数式编程特性-- Applicative

  • 直接对两个容器直接操作

    new Mabe(2).map(two => 
      new Maybe(3).map(add(two))
    ).flat();
    ​
    new Maybe(add)
        .ap(new Maybe(3))
        .ap(new Maybe(2));
    ​
    ap(other) {
        return other.map(this.$value);
    }
    

Identity: Maybe(id).ap(v)===v; Homomorphism: Maybe(f)ap(Maybe(x))===Maybe(f(x)); Interchange: v.ap(Maybe(x))===Maybe(f=>f(x)).ap(v) Composition: Maybe(compose)ap(u)ap(v)ap(w)===uap(vap(w));

响应式编程:

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

    • 数据流

    • 操作符

      • 过滤
      • 合并
      • 转化
      • 高阶

响应式编程特性-- Observable

  • 观察者模式: 观察者 --订阅--> 发布者,发布者-- 推送数据--> 观察者
  • 迭代器模式: 数据会持续不断的推送
  • Promise/EventTarget 超集*
const { fromEvent } = rxjs;
​
const clicks = fromEvent(document, 'click'); // Observableconst sub = clicks.subscribe(x => console.log(x)); //订阅数据setTimeout(() => sub.unsubscribe(),5000); //取消订阅

响应式编程特性-- 操作符

  • 响应式编程 :“compose”

    • 合并
    • 过滤
    • 转化
    • 异常处理
    • 多播
const { fromEvent, map } = rxjs;
const clicks = fromEvent(document, 'click');
const positions = clicks.pipe(
    map(ev => ev.clientx),// 转化操作符
    map(clientX => ++clientX)
);
​
positions.subscribe(x => console.log(x));
​
const events = [{ clientX: 1 },{ clientx: 2 }];
compose(
    map(clientX => ++clientX),
    map(ev=> ev.clientX)
)(events);

响应式编程特性-- Monad

  • 去除嵌套的 Observable
const { fromEvent, flatMap, fetch }=rxjs;
const clicks = fromEvent(document, 'click');
const users=clicks.pipe(
    flatMeple => fetch.fromFetch(
        //嵌套 Observable
        "https://api.github.com/users?perpage=5
    )),
    flatMap(res => res.json())//嵌套 Promise
);
users.subscribe(data => console.log(data));

04_什么是领域特定语言

  • Domain-specificlanguage(DSL):应用于特定领域的语言

    • HTML
    • SQL
  • General-purposelanguage

    • C/C++
    • Javascript ...
  • 语言运行

    • lexer -- > parse -- > vist -- > interpret / Code gen
  • 词法解析lexer:

    • SQL Token 分类 ·注释 关键字 操作符 空格 · 字符串 变量
  • 04_Parser_语法规则 上下文无关语法规则

  • <selectStatement>=SELECT<selectList>FROM<tableNme>
    <selectList> ::= <selectField> [,<selectList> ]
    <tableName>:=<tableName>[,<tableList>]
    

    推导式:表示非终结符到(非终结符或终结符)的关系。 终结符:构成句子的实际内容。可以简单理解为词法分析中的token。 非终结符:符号或变量的有限集合。它们表示在句子中不同类型的短语或子句。

04_Parser_LL

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

  • select name from user

    function selectstatement(node){
        node.children.push(matchToken('select'));
        const selectListNode ={ type: 'selectList', children: [] };//构建节点并递归
        node.children.push(selectListNode);
        selectList(selectListNode);
        node.children.push(matchToken('from'));
        node.children.push(tableName());
    }
    function selectList({ children }) {
        let node;
        while (node = selectField()){
            children.push(node);
        }
    }
    function selectField() {
        if (currentToken(().match(/\w+/)) {
            return { type: 'field', token: nextToken() };
        }
    }
    

    04_Parser_LR

    • LR:从左到右检查,从右到左构建语法树
    • select name from user
  • tools

  • visitor

附录 ·函数式编程 ·mostly-adequate.gitbook.io/mostly-adeq… ·ramdajs.com/ ·rxjs.dev/ ·创建DSL parser工具 ·pegjs.org/ ·github.com/antlr/antlr… ·github.com/yiminghe/ki…