javascript柯里化及组合函数~
大家是不是看我上篇博文有点蒙。用的的curry和compose是什么鬼,怎么那么神奇。上篇博文也是主要用到了这两个函数。
那今天我们来聊一下curry和compose,这两个东西是函数式编程很重要的东西,基本每个稍微复杂点的例子都要涉及这两个函数。
什么是curry呢?
---函数柯里化。就是这个东西了。举一个简单的例子。
var _console=curry(function(x){
console.log(x);
})
_console("hello"); //hello
其实就这个作用,先定义函数,后传参数。_console保存了这个函数,每次调用这个函数的时候传给它参数。是不是很简单呢?
看一下curry的实现。
function curry(fn){
return function(f){
return fn(f);
}
}
只是返回一个函数那么简单,这里还涉及fn的控制权反转,类似redux的dispatch,传入dispatch使用它。
那么当这个curry函数有2个参数的时候,curry就得变为:
function curry(fn){
return function(f){
return function(g){
return fn(f,g);
}
}
}
那么有3个参数呢?
function curry(fn){
return function(f){
return function(g){
return function(h){
return fn(f,g,h);
}
}
}
}
那么当我想一次传2个参数,剩下一个传1个参数怎么办?
function curry(fn){
return function(f){
return function(g,h){
return fn(f,g,h);
}
}
}
那么我改主意了,我不确定每次传多少个参数了,我不确定一共传多少个参数了。怎么办?
自动柯里化函数,你会发现_.curry完美的解决了这个问题,甚至可以在你传一个空参数的时候忽略它。
今天我们不研究_.curry的实现,有大神可以分享一下~
我们今天写一个自动柯里化函数。
怎么写?什么思路呢?它的难点在哪?
首先我们需要知道自动柯里化函数的执行过程和普通柯里化函数的执行过程是一样的,即有多少参数返回多少的function,取到函数的参数的长我们知道,
但是函数又不是数组,可以用一层for循环return出来,只能改变思路,函数的循环就是递归了。
它的难点在哪?
我们需要在每次执行一次()的时候获取他的参数,在递归里我们可以声明一个外部变量而储存每次执行的参数。
但是这个是不行的,因为每次递归都需要返回一个function,这意味着你无法返回上一层了。那我们只能把执行的参数当函数的参数传进去了。
直接把自动柯里化给你们(复制可测试)
function curry (fn, length, args) {
length = length || fn.length; //保存函数的参数数量
args = args || [];
return function(){
var _args = args.slice(0),
arg, i;
for(i = 0; i < arguments.length; i++) {//遍历_args,存储每次执行的参数
arg = arguments[i];
_args.push(arg);
}
if(_args.length < length) {
return curry.call(this, fn, length, _args); //递归调用自己
} else {
return fn.apply(this, _args);
}
}
}
可以看到解决return函数个数的是
if(_args.length < length) {
return curry.call(this, fn, length, _args);
}else {
return fn.apply(this, _args);
}
_arg储存了从开始到现在的所有的参数,如果参数”够了“(一个小知识,函数的length==函数参数的个数),就调用fn,把所有参数都传给他,
如果不够,就继续递归,递归的时候呢,把当前的参数传到下一层函数里。
难点就在于每次递归回来都要遍历一下当前的参数,把它push到_args里面。
这样做也避免了空参数的情况,当传入一个空参数,比如这样调用()。_args不变。
大家有空可以研究一下_.curry的实现过程是不是类似呢?-0-
下一个主角,compose,这个函数在fp里面叫组合函数,它能把里面的参数都组合起来,一起调用。类似流式。
function compose(fn1,fn2){
return function(arg){
fn2(fn1(arg));
}
}
var gen= compose(function(b){console.log(b);return b+"be deal with b";} , function(a){console.log(a);return a+"be deal with a";} )
gen("holle"); //hello hello deal with b;
gen就像一个水管,gen("holle");就是往水管里注入了水。
它会从compose左边的函数开始执行,hello是数据流,流过2个函数。第一个函数的返回值会传入到第二个参数继续执行。
在函数式编程里,compose是避免命令式的重要一环,fp要做到的每一个函数都是”纯“的,脏的东西交给使用者。把每个纯的函数组合到compose里面,就像gulp的pipe()一样,每个函数就像一个插件,来处理数据流。
那么我想从右到左处理数据流,
function compose(fn1,fn2){
return function(arg){
fn1(fn2(arg));
}
}
那么和curry一样,我不想只能传2个函数,我想传任意函数怎么办?
这里我们不写自动组合函数了,因为在我分析redux源码的时候发现了这个函数,redux帮我们写了~我们就只来分析一下就好了。
先上一下源码。redux不跟lodash一样一个函数跳来跳去,lodash想只看一部分是不好懂的。redux的compose函数(复制可测试)。
function compose() {
for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
funcs[_key] = arguments[_key]; //把参数都复制到funcs数组里。
}
return function () {
if (funcs.length === 0) {
return arguments.length <= 0 ? undefined : arguments[0]; //处理无参数的情况,返回undefined
}
var last = funcs[funcs.length - 1]; //这是最后一个处理函数
var rest = funcs.slice(0, -1); //取除了最后一个剩下的处理函数
return rest.reduceRight(function (composed, f) {//每次都执行当前处理函数传入基数
return f(composed);
}, last.apply(undefined, arguments)); //执行最后一个处理函数,返回值作为reduce的基数。
};
}
他的原理很简单,就是把arguments的所有函数都执行一次,把上个函数的返回值传入。这里巧妙的用了数组的reduceRight函数。
你换成reduce也可以,那这个last要换成first,这样就是从左到右执行啦。处理顺序的问题。
它俩是很有用的,例子可以看上篇博文~~点我。
学好这两个函数,我们再打开函数式编程的大门吧~
javascript柯里化及组合函数~的更多相关文章
- JavaScript函数式编程(纯函数、柯里化以及组合函数)
JavaScript函数式编程(纯函数.柯里化以及组合函数) 前言 函数式编程(Functional Programming),又称为泛函编程,是一种编程范式.早在很久以前就提出了函数式编程这个概念了 ...
- 【 js 基础 】【 源码学习 】柯里化和箭头函数
最近在看 redux 的源码,代码结构很简单,主要就是6个文件,其中 index.js 负责将剩余5个文件中定义的方法 export 出来,其他5个文件各自负责一个方法的实现. 大部分代码比较简单,很 ...
- JAVAScript柯里化、部分应用参数终极理解
一.柯里化 在定义柯里化.部分应用参数的概念前,首先必须对闭包有深入的了解和定义,闭包一句话说清楚:函数返回值为函数. 柯里化的定义:将多参函数分解为按步骤接受单个参数的函数,如下代码: var mo ...
- JavaScript柯里化(currying)
参考: https://www.jianshu.com/p/33392cb4b055 https://ruby-china.org/topics/38385 https://stackoverflow ...
- javascript 柯里化
先看一下代码 function add(){ var sum=0; for(var i=0;i<arguments.length;i++){ sum+=arguments[i]; } retur ...
- 函数柯里化实现sum函数
需求 实现sum函数,使其可以传入不定长参数,以及不定次数调用 //示例 console.log(sum(1,2)(3)()) //6 console.log(sum(2,3,4,5)(1,2)(3) ...
- javascript柯里化
function curry(fn){ var slice = Array.prototype.slice; var arr = slice.call(arguments,1); return fun ...
- 柯里化函数之Javascript
柯里化函数之Javascript 定义 依据定义来说,柯里化就是将一个接收"多个"參数的函数拆分成一个或者很多个接收"单一"參数的函数.定义看起来是比較抽象的. ...
- 浅析 JavaScript 中的 函数 currying 柯里化
原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...
随机推荐
- react购物车demo
import React, { Component } from 'react'; import './App.css'; import {connect} from 'react-redux'; i ...
- laravel依赖注入 容器
[看完就懂]Laravel 服务容器,IoC,DI DI DI就是常说的依赖注入,那么究竟什么是依赖注入呢? 打个比方,电脑(非笔记本哈)需要键盘和鼠标我们才能进行操作,这个‘需要’换句话说 ...
- C# Note11:如何优雅地退出WPF应用程序
前言 I should know how I am supposed to exit my application when the user clicks on the Exit menu item ...
- php7函数,声明,返回值等新特性介绍
使用 ... 运算符定义变长参数函数 (PHP 5 >= 5.6.0, PHP 7) 现在可以不依赖 func_get_args(), 使用 ... 运算符 来实现 变长参数函数. functi ...
- Airflow 使用随笔(内含 TimeZone 和 Backfill 等的详解)
其实怎么部署 airflow 又哪些特性,然后功能又是如何全面都可以在 Reference 的文章里面找到,都不是重点这里就不赘述了. 这里重点谈一下我在部署完成仔细阅读文档之后觉得可以总结的一些东 ...
- Canvas & SVG
Canvas & SVG https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-dev ...
- hdu-2072(字典树)
字典树模板题 代码 #include<iostream> #include<algorithm> #include<cstdio> #include<cstr ...
- Nginx 防盗链 secure_link 模块
L:76 需要通过 --with-http_secure_link_module 编译进Nginx secure_link 指令 Syntax: secure_link expression; Def ...
- linux硬件数据
Linux 文件详解 lrwxrwxrwx root root 8月 bin -> usr/bin //二进制目录 存放了许多GNU用户极工具 dr-xr-xr-x. root root 8月 ...
- kubernetes 一个服务的基本组成
1. service Service是kubernetes最核心的概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求进行负载分发到后端的各个容器应用上 k ...