【SSD系列】都说柯里化,反柯里化 , 不会不知道吧

简介: 柯里化和偏函数都是函数式编程里面重要的概念,我们今天来来点不一样的 反柯里化。不过既然是反柯里化,就先了解一下其姊妹 柯里化和偏函数。

1.JPG

前言


柯里化偏函数都是函数式编程里面重要的概念,我们今天来来点不一样的 反柯里化

不过既然是反柯里化,就先了解一下其姊妹 柯里化和偏函数


柯里化 和偏函数


1.1 柯里化


维基百科上说道:


柯里化,英语:Currying,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。


这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。


还是看个例子吧,比较直观:


function sum(a, b, c) {
  return a + b + c;
}
function currySum(a){
    return function (b){
        return function (c){
            return a + b + c;
        }
    }
}
sum(1,2,3) // 6
currySum(1)(2)(3) // 6
复制代码


简单点说就两点:


  1. 多段传参
  2. 返回的函数,满足条件就是执行函数,返回结果


1.1.1 难点


通用的柯里化函数难点就是在于如何知道函数参数的长度, 常用的手段是:


  1. Function.prototype.length属性获取参数长度
    缺点嘛,rest参数是不计算在长度里面的


下面的代码,获取的length是2就是最好的事实。

function log(msg1, msg2, ...other){
    console.log(msg1, msg2, ...other);
}
console.log(log.length); // 2
复制代码


  1. 柯里化的时候,显示的传入参数的长度
  2. 当然可以综合两者,一个函数默认值就ok了


function curry(fn, length = fn.length){
    // ....
}
复制代码


1.1.2 占位符


柯里化后来多个一个占位符的概念,啥意思,就是,这个参数我先不传,后面再传入。

看个lodash官方的例子。


var abc = function(a, b, c) {
  return [a, b, c];
};
var curried = _.curry(abc);
// Curried with placeholders.
curried(1)(_, 3)(2);
// => [1, 2, 3]
复制代码


至于实现嘛,高级版本lodash.curry,平民版本 JavaScript专题之函数柯里化


1.2 偏函数


和柯里化有相似之处,简单理解参数两次传递


  1. 第一次固定部分参数
  2. 第二次传入剩余参数


看个underscore官方的例子,这个例子也 出现了占位符,不过不影响理解。


var subtract = function(a, b) { return b - a; };
sub5 = _.partial(subtract, 5);
sub5(20);
=> 15
// Using a placeholder
subFrom20 = _.partial(subtract, _, 20);
subFrom20(5);
=> 15
复制代码


至于实现, 高级版本 lodash.partial 以及underscore.partial, 贫民版本JavaScript专题之偏函数


到这里,偏函数的功能和bind有相似之处。



不过,这些都不是今天的重点,今天的重点是反柯里化。


反柯里化


2.1 概念


反柯里化是一种拿来主义,把别人的东西拿过来用。

先看个例子: 我们常用来判断数据类型的Object.prototype.toString


function unCurry(fn) {
    return function (context) {
        return fn.apply(context, Array.prototype.slice.call(arguments, 1));
    }
}
// 不使用反柯里化
Object.prototype.toString.call({});  // [object Object]
// 反柯里化
const toString = unCurry(Object.prototype.toString);
toString({});        // [object Object]
toString(() => { }); // [object Function]
toString(1);         // [object Number]
复制代码


2.2 实现


2.2.1 基础版本:简单好理解


function unCurry(fn) {
    return function (context) {
        return fn.apply(context, Array.prototype.slice.call(arguments, 1));
    }
}
复制代码


2.2.2 原型版本: 入侵 + 便利


Function.prototype.unCurry = function(){
    var self = this;
    return function(){
        return Function.prototype.call.apply(self, arguments);
    }
}
复制代码


这个版本入侵了原型,我不喜欢。 便利性倒还是有的。


理解上的难点两个


  1. self = this
    self等于函数本身,这里就是暂存函数
  2. Function.prototype.call.apply(self, arguments)


说起来费事,看下面的转换吧。


Function.prototype.call.apply(self, arguments)
==>
Function.prototype.call.bind(self)(arguments)
==>
self.call(arguments)
==>
self.call(arguments[0], arguments[1-n]) // arguments[0]就是self函数的上下文了
复制代码


使用的话,也会略有变化


2.2.3 原型版本2


这个我不做解读,大家自行理解一波


Function.prototype.unCurry = function () {
    return this.call.bind(this);
};
复制代码


借助ES6, 下面的代码是不是也可以呢?


Function.prototype.unCurry = function () {
    return (...args) => this.call(...args)
};
复制代码


当前还有各种好玩的写法,更多详情可以参考# 柯里化&&反柯里化


2.3 使用场景


反柯里化是一种思路,其实现肯定是可以被其他方案替代的,但是多一种思路,就多一种手段


2.3.1 判断数据类型


上面的demo已经演示过了。


2.3.2 数组push(高级编程中的例子)


const push = unCurry(Array.prototype.push);
const arr = [1, 2, 3];
push(arr, 4, 5, 6);
console.log(arr);
复制代码


2.3.3 复制数组


const clone = unCurry(Array.prototype.slice);
var a = [1,2,3];
var b = clone(a);
console.log("a==b", a === b);  // a==b false
console.log(a, b);  // [ 1, 2, 3 ] [ 1, 2, 3 ]
复制代码


2.3.4 发送事件


const dispatch = unCurry(EventTarget.prototype.dispatchEvent);
    window.addEventListener("event-x", (ev) => {
        console.log("event-x", ev.detail);  // event-x ok
    })
    dispatch(window, new CustomEvent("event-x", { detail: "ok" }));
复制代码

详解JS函数柯里化

相关文章
|
11月前
|
机器学习/深度学习 算法 TensorFlow
交通标志识别系统python+TensorFlow+算法模型+Django网页+数据集
交通标志识别系统python+TensorFlow+算法模型+Django网页+数据集
96 0
|
11月前
|
存储 分布式计算 Java
详细探讨在线上环境中慎用BigDecimal的原因和可能的替代方案
详细探讨在线上环境中慎用BigDecimal的原因和可能的替代方案
135 0
详细探讨在线上环境中慎用BigDecimal的原因和可能的替代方案
|
4月前
|
Kubernetes 搜索推荐 应用服务中间件
通过keepalived+nginx实现 k8s apiserver节点高可用
通过keepalived+nginx实现 k8s apiserver节点高可用
257 17
|
机器学习/深度学习 缓存 并行计算
NVIDIA Tesla GPU系列P4、T4、P40以及V100参数性能对比
NVIDIA Tesla系列GPU适用于高性能计算(HPC)、深度学习等超大规模数据计算,Tesla系列GPU能够处理解析PB级的数据,速度比使用传统CPU快几个数量级,NVIDIA Tesla GPU系列P4、T4、P40以及V100是Tesla GPU系列的明星产品,云服务器吧分享NVIDIA Tesla GPU系列P4、T4、P40以及V100参数性能对比:
|
JavaScript 前端开发 开发者
探索Vue 3世界中的12个流行组件库
探索Vue 3世界中的12个流行组件库
292 0
|
存储 数据库 索引
64.【clickhouse】ClickHouse从入门到放弃-二级索引
【clickhouse】ClickHouse从入门到放弃-二级索引
64.【clickhouse】ClickHouse从入门到放弃-二级索引
|
搜索推荐 JavaScript 前端开发
网络优化中怎么减轻蜘蛛的抓取?
还在等什么,快来一起讨论关注吧,公众号【八点半技术站】,欢迎加入社群
|
XML 存储 JSON
API参考—参数管理—ModifyParameter
调用ModifyParameter接口修改参数值。
172 0
NSTextField控件应用详解
NSTextField控件应用详解
451 0
NSTextField控件应用详解
|
存储 PHP 数据安全/隐私保护
SSO单点登录三种情况的实现方式详解
单点登录(SSO——Single Sign On)对于我们来说已经不陌生了。对于大型系统来说使用单点登录可以减少用户很多的麻烦。就拿百度来说吧,百度下面有很多的子系统——百度经验、百度知道、百度文库等等,如果我们使用这些系统的时候,每一个系统都需要我们输入用户名和密码登录一次的话,我相信用户体验肯定会直线下降。
1567 0