实现compose的五种思路

2023-11-09

好久没有更新了,最近学习的过程中一直在用联想的思维来去看问题,javascript是一门非常灵活的语言,集合了好多语言的特性和多种编程模式,对于compose的实现,就有非常多的思路,每一种思路都有自己的特点,实现之后,有种殊途同归的快感。下面就是我总结的实现compose函数的五种思路。

  • 面向过程
  • 函数组合(reduce)
  • 函数交织(AOP编程)
  • Promise(sequence)
  • Generator(yield)

什么是compose

简单回顾一下composecompose就是执行一系列的任务(函数),比如有以下任务队列,

let tasks = [step1, step2, step3, step4]

每一个step都是一个步骤,按照步骤一步一步的执行到结尾,这就是一个compose
compose在函数式编程中是一个很重要的工具函数,在这里实现的compose有三点说明

  • 第一个函数是多元的(接受多个参数),后面的函数都是单元的(接受一个参数)
  • 执行顺序的自右向左的
  • 所有函数的执行都是同步的(异步的后面文章会讲到)

还是用一个例子来说,比如有以下几个函数

let init = (...args) => args.reduce((ele1, ele2) => ele1 + ele2, 0)
let step2 = (val) => val + 2
let step3 = (val) => val + 3
let step4 = (val) => val + 4

这几个函数组成一个任务队列

steps = [step4, step3, step2, init]

使用compose组合这个队列并执行

let composeFunc = compose(...steps)

console.log(composeFunc(1, 2, 3))

执行过程
6 -> 6 + 2 = 8 -> 8 + 3 = 11 -> 11 + 4 = 15
所以流程就是从init自右到左依次执行,下一个任务的参数是上一个任务的返回结果,并且任务都是同步的,这样就能保证任务可以按照有序的方向和有序的时间执行。

实现compose的五种思路

所有思路的执行过程都是上面的例子,以下只讲compose实现

面向过程

这个思路就是使用递归的过程思想,不断的检测队列中是否还有任务,如果有任务就执行,并把执行结果往后传递,这里是一个局部的思维,无法预知任务何时结束。直观上最容易结束和理解。

const compose = function(...args) {
  let length = args.length
  let count = length - 1
  let result
  return function f1 (...arg1) {
    result = args[count].apply(this, arg1)
    if (count <= 0) {
      count = length - 1
      return result
    }
    count--
    return f1.call(null, result)
  }
}

代码地址

函数组合

这个思路是一种函数组合的思想,将函数两两组合,不断的生成新的函数,生成的新函数挟裹了函数执行的逻辑信息,然后再两两组合,不断的传递下去,这种思路可以提前遍历所有任务,将任务组合成一个可以展开的组合结构,最后执行的时候就像推导多米诺骨牌一样。

函数的组合过程

f1 = (...arg) => step2.call(null, init.apply(null, arg))
f2 = (...arg) => step3.call(null, f1.apply(null, arg))
f3 = (...arg) => step4.call(null, f2.apply(null, arg))

compose实现

const _pipe = (f, g) => (...arg) => g.call(null, f.apply(null, arg))
const compose = (...args) => args.reverse().reduce(_pipe, args.shift())

代码地址

函数交织(AOP)

这个实现的灵感来自javascript设计模式中的高阶函数,因为compose的任务在本质上就是函数执行,再加上顺序,所以可以把实现顺序执行放到函数本身,对函数的原型进行方法的绑定。方法的作用对象是函数,面向对象封装的数据,面向函数封装的是函数的行为。

需要对函数绑定两个行为 beforeafterbefore执行函数多元部分(启动),after执行函数单元部分

Function.prototype.before = function(fn) {
  const self = this
  return function(...args) {
    let result = fn.apply(null, args)
    return self.call(null, result)
  }
}

Function.prototype.after = function(fn) {
  const self = this
  return function(...args) {
    let result = self.apply(null, args)
    return fn.call(null, result)
  }
}

这里对函数进行方法的绑定,返回的是带着函数执行的规则的另外一个函数,在这里是次序的排列规则,对返回的函数依然可以进行链式调用。
compose实现

const compose = function(...args) {
  let before = args.pop()
  let start = args.pop()
  if (args.length) {
    return args.reduce(function(f1, f2) {
      return f1.after(f2)
    }, start.before(before))
  }
  return start.before(before)
}

函数执行过程

step2.before(init).after(step3).after(step4)
fn3.after(step4)
fn3 = fn2.after(step3)
fn2 = fn1.before(step1)
fn1 = init -> step2 -> step3 -> step4

代码地址

Promise

ES6引入了PromisePromise可以指定一个sequence,来规定一个执行then的过程,then函数会等到执行完成后,再执行下一个then的处理。启动sequence可以使用
Promise.resolve()这个函数。构建sequence可以使用reduce
compose实现

const compose = function(...args) {
  let init = args.pop()
  return function(...arg) {
    return args.reverse().reduce(function(sequence, func) {
      return sequence.then(function(result) {
        return func.call(null, result)
      })
    }, Promise.resolve(init.apply(null, arg)))
  }
}

代码地址

Generator

Generator主要使用yield来构建协程,采用中断,处理,再中断的流程。可以事先规定好协程的执行顺序,然后再下次处理的时候进行参数(结果)交接,有一点要注意的是,由于执行的第一个next是不能传递参数的,所以第一个函数的执行需要手动调用,再空耗一个next,后面的就可以同步执行了。
generator构建


function* iterateSteps(steps) {
  let n
  for (let i = 0; i < steps.length; i++) {
    if (n) {
      n = yield steps[i].call(null, n)
    } else {
      n = yield
    }
  }
}

compose实现

const compose = function(...steps) {
  let g = iterateSteps(steps)
  return function(...args) {
    let val = steps.pop().apply(null, args)
    // 这里是第一个值
    console.log(val)
    // 因为无法传参数 所以无所谓执行 就是空耗一个yield
    g.next()
    return steps.reverse.reduce((val, val1) => g.next(val).value, val)
  }
}

代码地址

总结

github地址

github里面针对每一种实现包含了完成的demo案例,就在test.js里面,以上就是实现同步compose的五种思路,每一种思路的出发点都不一样,但是实现的目的都是一样的,可以看出javascript是非常灵活的,借助es6PromiseGenerator也可以很优雅的实现。后面会介绍compose的异步实现,在函数式编程来看,异步的本质应该就是Monad

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实现compose的五种思路 的相关文章

随机推荐

  • IGBT基本工作原理及IGBT的作用是什么?

    IGBT 今天我们一起来了解关于IGBT 绝缘栅双极性晶体管 芯片 在过去的几十年中 我们生活的每个角落都离不开能源的驱动 然而 传统的功率晶体管却受限于一些方面不足 幸运的是 IGBT芯片的出现彻底改变了这一局面 IGBT和MOS管都是一
  • JavaWeb01-Servlet-thymeleaf-mvd-dispatcher-controller

    Servlet servlet的基本作用 第一个servlet程序 演示servlet的生命周期 public class Demo02Servlet extends HttpServlet Override public void ini
  • CodeGeeX使用指南

    CodeGeeX是一个具有130亿参数的多编程语言代码生成预训练模型 使用超过二十种编程语言训练得到 基于CodeGeeX开发的插件可以实现通过描述生成代码 补全代码 代码翻译等一系列功能 CodeGeeX同样提供可以定制的提示模式 Pro
  • 华为OD机试真题-组装数组【2023Q1】

    题目内容 给你一个整数M和数组N N中的元素为连续整数 要求根据N中的元素组装成新的数组R 组装规则 1 R中元素总和加起来等于M 2 R中的元素可以从N中重复选取 3 R中的元素最多只能有1个不在N中 且比N中的数字都要小 不能为负数 输
  • 1012 数字分类

    1012 数字分类 题目 输入格式 输入样例 输出样例 代码 小结 题目 给定一系列正整数 请按要求对数字进行分类 并输出以下 5 个数字 A 1
  • sql 选取前两条_从零开始学习SQL(二)SQL简单查询

    SQL简单语句可用于查询数据库中的内容 数据来源 SQLZOO SELECT from WORLD Tutorial zh sqlzoo net 一 基本查询语句 从数据库中查询数据时 需要使用SELECT语句 通常SELECT语句可用于指
  • 【滤波器】1. 一阶RC滤波器

    文章目录 1 低通滤波器 2 高通滤波器 3 常用工具 1 低通滤波器 电容上的分压 U C U I
  • shell awk 入门

    awk 好用的数据处理工具 awk 也是一个非常棒的数据处理工具 sed 常常用于一整个行的处理 awk 则比较倾向于一行当中分成数个 栏位 或者称为一个域 也就是一列 来处理 因此 awk 相当的适合处理小型的数据数据处理呢 awk 通常
  • 在Linux系统中如何把文件拷贝到U盘

    Linux下把所有的都当成文件处理 如果在linux系统下需要拷贝文件 哪么你需要先把U盘挂载到系统中的某一个位置 然后再使用cp命令完成拷贝 工具原料 Linux操作系统一台 U盘一枚 方法 步骤 1 把U盘插入Linux电脑 确保U盘指
  • 用C语言编写的推箱子小游戏

    大一时候的期末大作业 一直没找到 在桌子缝里找到了优盘 现在才把内容放上来 希望能帮到你们 具体内容分为实验报告和程序具体代码 程序设计基础 期末大作业报告 实验设计 1 编写推箱子小游戏 2 使用C语言 3 使用Dev C 编程程序 4
  • 7. AES加密之base64编码

    整个加密的流程 编码算法 base64算法 与加密解密无关 只是对数据进行编码 方便在网络间进行传输 import org junit jupiter api Test import java nio charset StandardCha
  • SpringBoot+EasyCaptcha实现验证码功能

    一 EasyCaptcha简介 Java图形验证码 支持gif 中文 算术等类型 可用于Java Web JavaSE等项目 开源地址 https github com whvcse EasyCaptcha 二 SpringBoot项目如何
  • 华为OD机试真题-最多获得的短信条数【2023.Q1】

    题目内容 某云短信厂商 为庆祝国庆 推出充值优惠活动 现在给出客户预算 和优惠售价序列 求最多可获得的短信总条数 输入描述 第一行客户预算M 其中 0 M 10 6 第二行给出售价表 P1 P2 Pn 其中 1 n 100 Pi为充值 i
  • python3 在线解析jpg图片或解析本地jpg,判断jpg是否损坏

    一 JPEG图片格式详解 JPEG图片格式组成部分 SOI 文件头 APP0 图像识别信息 DQT 定义量化表 SOF0 图像基本信息 DHT 定义Huffman表 DRI 定义重新开始间隔 SOS 扫描行开始 EOI 文件尾 二 数据结构
  • SIEM的内容

    20200920 昨天看到了与SIEM相关的内容 但是除了一篇文章给我讲解了他们部分的SOC架构与方案 这个算是SIEM的升级版吧 其他的文章都没有给我非常好的反馈 这里来记录一下 文章列表 1 Security Correlation T
  • 反射与泛型

    文章目录 Class API demo reflect set action Class API getField getField String name 能获取 public 的字段 包括父类的 getDeclaredField Str
  • Linux E:Could not get lock /var/lib/dpkg/lock - open (11:Resource temorarily unavailable)

    1 错误提示如下图所示 2 解决方法 1 找到并杀掉所有的apt get和apt进程 运行命令 ps A grep apt 2 3 使用命令 sudo kill 9 进程号 或者 sudo kill SIGKILL 进程号 来关闭所有进程
  • 使用mysqldump命令导出指定数据库的数据+Java实现数据库的一键导出备份

    由于项目中需要用到在页面添加一个一键数据备份的功能 所以选择使用mysqldump命令的方法 用java的运行时类Java lang Runtime来使用后台窗口 记录下整个实现的过程 1 首先mysqldump命令需要在mysql的bin
  • linux管理控制面板--可视化管理linux

    市面上主要的网站主机分为三大类 具体差异简单说明 虚拟主机 也是大家口中说的 主机空间 可定制化低 适合纯小白和新手 完全不懂技术 说明白点 就是一台云服务器分割空间和内存形成的主机空间 可以用来搭建网站 但是管理权限受到主机厂商一些限制
  • 实现compose的五种思路

    好久没有更新了 最近学习的过程中一直在用联想的思维来去看问题 javascript是一门非常灵活的语言 集合了好多语言的特性和多种编程模式 对于compose的实现 就有非常多的思路 每一种思路都有自己的特点 实现之后 有种殊途同归的快感