函数式编程 - 组合compose的使用方法

本文介绍了函数式编程中的函数组合概念,特别是compose函数,展示了如何通过简单的示例和递归、reduce等方式实现函数组合,以及其与pipe的区别。作者还提到了在实际开发中如Redux和Koa等框架中的应用实例。
摘要由CSDN通过智能技术生成

函数式编程中有一个比较重要的概念就是函数组合(compose),组合多个函数,同时返回一个新的函数。调用时,组合函数按顺序从右向左执行。右边函数调用后,返回的结果,作为左边函数的参数传入,严格保证了执行顺序,这也是compose 主要特色。| 函数式编程--函数组合(Function composition) - 知乎

入门简介


组合两个函数

compose 非常简单,通过下面的示例代码,就非常清楚。

function compose (f, g) {
    return function(x) {
        return f(g(x));
    }
}

var arr = [1, 2, 3],
reverse = function(x){ return x.reverse()},
getFirst = function(x) {return x[0]},
compseFunc = compose(getFirst, reverse);
    
compseFunc(arr);   // 3

参数在函数间就好像经过‘管道’传输同样,最右边的函数接收外界参数,返回结果传给左边的函数,最后输出结果。

组合任意个函数

上面组合了两个函数的compose,也让咱们了解了组合的特点,接着咱们看看如何组合更多的函数,由于在实际应用中,不会像入门介绍的代码那么简单。

主要注意几个关键点:

1、利用arguments的长度得到所有组合函数的个数

2、reduce 遍历执行全部函数。

var compose = function() {
      var args = Array.prototype.slice.call(arguments);
      
      return function(x) {
       if (args.length >= 2) {
       
          return args.reverse().reduce((p, c) => {
            return p = c(p)
         }, x)
         
       } else {
           return args[1] && args[1](x);
       }
      }
    }
   
// 利用上面示例 测试一下。
var arr = [1, 2, 3],
reverse = function(x){ return x.reverse()},
getFirst = function(x) {return x[0]},
trace = function(x) {  console.log('执行结果:', x); return x}
    
compseFunc = compose(trace, getFirst, trace, reverse);
    
compseFunc(arr);   
 // 执行结果: (3) [3, 2, 1]
 // 执行结果: 3
 // 3

如此实现,基本没什么问题,变量arr 在管道中传入后,经过各种操作,最后返回了结果。

深入理解


认识pipe

函数式编程(FP)里面跟compose类似的方法,就是pipe

pipe,主要作用也是组合多个函数,称之为, 肯定得按照正常方法,从左往右调用函数,与compose 调用方法相反。

ES6 实现Compose function

先看下compose 最基础的两参数版本

const compose = (f1, f2) => value => f1(f2(value));

利用箭头函数,非常直接的表明两个函数嵌套执行的关系,接着看多层嵌套。

(f1, f2, f3...) => value => f1(f2(f3));

抽象出来表示:

() => () => result;

先提出这些基础的组合方式,对我们后面理解高级ES6方法实现compose有很大帮助。

实现pipe

前面提到 pipe 是反向的compose,pipe正向调用也致使它实现起来更容易。

pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)

一行代码就实现了pipe,套用上面抽象出来的表达式,reduce恰好正向遍历全部函数, 参数x做为传递给函数的初始值, 后面每次f(v)执行的结果,作为下一次f(v)调用的参数v,完成了函数组合调用。

或者,能够把函数组合中,第一个函数获取参数后,获得的结果,作为reduce遍历的初始值。

pipe = (fn,...fns) => (x) => fns.reduce( (v, f) => f(v), fn(x));

利用ES6提供的rest 参数 ,用于获取函数的多余参数,提取出第一个函数fn,多余函数参数放到fns中,fns看成是数组,也不用像arguments那种事先经过Array.prototype.slice.call转为数组,arguments对性能损耗也可以避免。 fn(x) 第一个函数执行结果做为reduce 初始值。

注:关于剩余参数rest,可参考我之前文章:【JS高级】ES6参数增强之剩余参数的应用

实现compose

1、pipe 部分,利用reduce实现,反过来看,compose就能够利用reduceRight

compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

2、利用递归

compose = (fn, ...fns) => fns.length === 0 ? fn: (...args) => fn(compose(...fns)(...args))

递归代码,首先看出口条件, fns.length === 0,最后一定执行最左边的函数,然后把剩下的函数再经过compose调用,

3、利用reduce实现。

一行实现,并且仍是用正向的 reduce

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))

作者其实用例子作了解释,可以看下reduce 迭代的方向是从左往右的,而compose 要求执行的方向是从右往左。对数组中每一项执行函数,正常状况下都应该放回执行结果,比如(v, f) => f(v),返回f(v)执行结果,这里是(f, g) => (...args) => f(g(...args))返回一个函数(...args) => f(g(...args)),这样就能够保证后面的函数g在被作为参数传入时比前面的函数f先执行。

简单利用前面的组合两个函数的例子分析一下。

...
composeFunc = compose(getFirst, trace, reverse);
composeFunc(arr);

主要看reduce 函数里面的执行过程:

◆ 入口 composeFunc(arr), 第一次迭代,reduce函数执行 (getFirst, trace) => (...args)=>getFirst(trace(...args)),函数(...args)=>getFirst(trace(...args))作为下一次迭代中累计器f的值。

◆ 第二次迭代,reduce函数中

f == (...args)=>getFirst(trace(...args))
g == reverse。
// 替换一下 (f, g) => (...args) => f(g(...args))
((...args)=>getFirst(trace(...args)), reverse) => (...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))

◆ 迭代结束,最后得到的comoseFunc就是

// 对照第二次的执行结果, (...args) => f(g(...args))

(...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))

◆ 调用函数composeFunc(arr)。

(arr) => ((...args)=>getFirst(trace(...args)))(reverse(arr))

===》reverse(arr) 执行结果[3, 2, 1] 做为参数

 ((...args)=>getFirst(trace(...args)))([3,2,1])

==》入参调用函数

   getFirst(trace[3,2,1])

===》 

   getFirst([3, 2, 1])

===》

   结果为 3

非常巧妙的把后一个函数的执行结果作为包裹着前面函数的空函数的参数,传入执行。其中大量用到下面的结构。

((arg)=> f(arg))(arg) 
// 转换一下。
  (function(x) {
     return f(x)
  })(x)

最后

不管是compose, 仍是后面提到的pipe,概念很是简单,均可以使用很是巧妙的方式实现(大部分使用reduce),并且在编程中很大程度上简化代码。最后列出优秀框架中使用compose的示例:

参考连接

参考资料:

JS高级编程中compose函数的介绍和基本实现 |  复合函数compose函数的概念

compose函数 | 函数式编程--函数组合(Function composition)函数式编程之compose

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue 3 支持函数式编程的特性,这意味着你可以在 Vue 组件中使用一些函数式编程的概念和技术。下面是一些与函数式编程相关的特性和用法: 1. 纯函数函数式编程鼓励使用函数,这意味着函数的输出只依赖于输入,不会对外部环境产生副作用。在 Vue 组件中,你可以将计算属性、过滤器和方法尽量设计成纯函数,以便更好地进行单元测试和调试。 2. 函数组合:Vue 3 提供了 `compose` 函数,它可以将多个函数组合起来,形成一个新的函数。你可以使用 `compose` 来简化复杂的逻辑处理,提高代码的可读性和可维护性。 3. 高阶组件 (Higher-Order Components):在 Vue 3 中,你可以使用函数式组件 (Functional Components) 来定义高阶组件。函数式组件是一种纯函数,接收一个 props 对象作为参数,并返回一个 VNode。通过使用函数式组件,你可以轻松地将一些常见的逻辑和功能封装成可复用的高阶组件。 4. 不可变数据:函数式编程鼓励使用不可变的数据结构,这意味着数据一旦创建就不能被修改。在 Vue 3 中,你可以使用 `ref` 和 `reactive` 来创建响应式的数据,但是不能直接修改它们的值。如果需要修改数据,应该通过使用特定的方法函数来产生新的数据。 5. 组件复用:函数式编程鼓励组件的复用性,你可以将一些通用的功能封装成可复用的函数或组件,并在不同的地方进行使用。Vue 3 中的函数式组件和高阶组件是实现组件复用的一种方式。 这些是 Vue 3 中支持函数式编程的一些特性和用法,希望对你有所帮助!如果还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

儒雅的烤地瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值