当前位置: 首页 > news >正文

三言两语说透柯里化和反柯里化

JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是两种很有用的技术,可以帮助我们写出更加优雅、泛用的函数。本文将首先介绍柯里化的概念、实现原理和应用场景,然后介绍反柯里化的概念、实现原理和应用场景,通过大量的代码示例帮助读者深入理解这两种技术的用途。

JavaScript中的柯里化

概念

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由数学家Haskell Curry命名。

简单来说,柯里化可以将使用多个参数的函数转换成一系列使用一个参数的函数。例如:

function add(a, b) {return a + b; 
}// 柯里化后
function curriedAdd(a) {return function(b) {return a + b;}
}

实现原理

实现柯里化的关键是通过闭包保存函数参数。以下是柯里化函数的一般模式:

function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);} else {return function(...args2) {return curried.apply(this, args.concat(args2));}}}
}

curry函数接受一个fn函数为参数,返回一个curried函数。curried函数检查接收的参数个数args.length是否满足fn函数需要的参数个数fn.length。如果满足,则直接调用fn函数;如果不满足,则继续返回curried函数等待接收剩余参数。

这样通过闭包保存每次收到的参数,直到参数的总数达到fn需要的参数个数,然后将保存的参数全部 apply fn执行。

利用这个模式可以轻松将普通函数柯里化:

// 普通函数
function add(a, b) {return a + b;
} // 柯里化后
let curriedAdd = curry(add); 
curriedAdd(1)(2); // 3

应用场景

  1. 参数复用

柯里化可以让我们轻松复用参数。例如:

function discounts(price, discount) {return price * discount;
}// 柯里化后
const tenPercentDiscount = discounts(0.1); 
tenPercentDiscount(500); // 50
tenPercentDiscount(200); // 20
  1. 提前返回函数副本

有时我们需要提前返回函数的副本给其他模块使用,这时可以用柯里化。

// 模块A
function ajax(type, url, data) {// 发送ajax请求
}// 柯里化后
export const getJSON = curry(ajax)('GET');// 模块B
import { getJSON } from './moduleA'; getJSON('/users', {name: 'John'});
  1. 延迟执行

柯里化函数在调用时并不会立即执行,而是返回一个函数等待完整的参数后再执行。这让我们可以更加灵活地控制函数的执行时机。

let log = curry(console.log);log('Hello'); // 不会立即执行setTimeout(() => {log('Hello'); // 2秒后执行
}, 2000);

JavaScript中的反柯里化

概念

反柯里化(Uncurrying)与柯里化相反,它将一个接受单一参数的函数转换成接受多个参数的函数。

// 柯里化函数  
function curriedAdd(a) {return function(b) {return a + b;}
}// 反柯里化后
function uncurriedAdd(a, b) {return a + b; 
}

实现原理

反柯里化的关键是通过递归不停调用函数并传入参数,Until参数的数量达到函数需要的参数个数。

function uncurry(fn) {return function(...args) {let context = this;return args.reduce((acc, cur) => {return acc.call(context, cur); }, fn);}
}

uncurry 接收一个函数 fn,返回一个函数。这个函数利用reduce不停调用 fn 并传入参数,Untilargs所有参数都传给 fn

利用这个模式可以轻松实现反柯里化:

const curriedAdd = a => b => a + b;const uncurriedAdd = uncurry(curriedAdd);
uncurriedAdd(1, 2); // 3

应用场景

  1. 统一接口规范

有时我们会从其他模块接收到一个柯里化的函数,但我们的接口需要一个普通的多参数函数。这时可以通过反柯里化来实现统一。

// 模块A导出
export const curriedGetUser = id => callback => {// 调用callback(user)
};// 模块B中
import { curriedGetUser } from './moduleA';// 反柯里化以符合接口
const getUser = uncurry(curriedGetUser); getUser(123, user => {// use user
});
  1. 提高参数灵活性

反柯里化可以让我们以任意顺序 passes 入参数,增加了函数的灵活性。

const uncurriedLog = uncurry(console.log);uncurriedLog('a', 'b'); 
uncurriedLog('b', 'a'); // 参数顺序灵活
  1. 支持默认参数

柯里化函数不容易实现默认参数,而反柯里化后可以方便地设置默认参数。

function uncurriedRequest(url, method='GET', payload) {// 请求逻辑
}

大厂面试题解析

实现add(1)(2)(3)输出6的函数

这是一道典型的柯里化面试题。解析:

function curry(fn) {return function curried(a) {return function(b) {return fn(a, b);}}
}function add(a, b) {return a + b;
}const curriedAdd = curry(add);curriedAdd(1)(2)(3); // 6

利用柯里化技术,我们可以将普通的 add 函数转化为 curriedAdd,它每次只接收一个参数,并返回函数等待下一个参数,从而实现了 add(1)(2)(3) 的效果。

实现单参数compose函数

compose函数可以将多个函数合并成一个函数,这也是一道常见的柯里化面试题。解析:

function compose(fn1) {return function(fn2) { return function(x) {return fn1(fn2(x));};};
}function double(x) {return x * 2;
}function square(x) {return x * x;
}const func = compose(double)(square);func(5); // 50

利用柯里化,我们创建了一个单参数的 compose 函数,它每次返回一个函数等待下一个函数参数。这样最终实现了 compose(double)(square) 的效果。

反柯里化Function.bind

Function.bind 函数实现了部分参数绑定,这本质上是一个反柯里化的过程。解析:

Function.prototype.uncurriedBind = function(context) {const fn = this;return function(...args) {return fn.call(context, ...args);} 
}function greet(greeting, name) {console.log(greeting, name);
}const greetHello = greet.uncurriedBind('Hello');
greetHello('John'); // Hello John

uncurriedBind 通过递归调用并传参实现了反柯里化,使 bind 参数从两步变成一步传入,这也是 Function.bind 的工作原理。

总结

柯里化和反柯里化都是非常有用的编程技巧,让我们可以写出更加灵活通用的函数。理解这两种技术的实现原理可以帮助我们更好地运用它们。在编码中,我们可以根据需要决定是将普通函数柯里化,还是将柯里化函数反柯里化。合理运用这两种技术可以大大提高我们的编程效率。

相关文章:

三言两语说透柯里化和反柯里化

JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是两种很有用的技术,可以帮助我们写出更加优雅、泛用的函数。本文将首先介绍柯里化的概念、实现原理和应用场景,然后介绍反柯里化的概念、实现原理和应用场景,通过大量的代码示例帮助读…...

细讲TCP三次握手四次挥手(四)

常见面试题 为什么TCP连接的时候是3次?2次不可以吗? 因为需要考虑连接时丢包的问题,如果只握手2次,第二次握手时如果服务端发给客户端的确认报文段丢失,此时服务端已经准备好了收发数(可以理解服务端已经连接成功)据…...

HarmonyOS/OpenHarmony元服务开发-配置卡片的配置文件

卡片相关的配置文件主要包含FormExtensionAbility的配置和卡片的配置两部分: 1.卡片需要在module.json5配置文件中的extensionAbilities标签下,配置FormExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称…...

mac安装nacos,M1芯片

第一步,官网下载 》nacos官网 去github中下载对应的版本,本人下载的是1.4.1版本 在这儿选择其他的版本,下面这里选择 tar.gz 压缩包 解压后放到一个非中文的目录下,我选择在 user目录下面创建一个other目录,将使用的环…...

老板说把跳针改过去,什么是主板跳针

最近骑车拍了很多视频,把电脑磁盘堆满了,想着买一条固态SSD卡扩展一下。 一咬牙一跺脚,直接安排,毫不犹豫。顺带加装了无限网卡和蓝牙5.2。 收到后立马安装。安装完发现识别不到新磁盘 确认安装没问题。然后就去问固态硬盘的客服 …...

PyTorch代码实战入门

人这辈子千万不要马虎两件事 一是找对爱人、二是选对事业 因为太阳升起时要投身事业 太阳落山时要与爱人相拥 一、准备数据集 蚂蚁蜜蜂数据集 蚂蚁蜜蜂的图片,文件名就是数据的label 二、使用Dataset加载数据 打开pycharm,选择Anaconda创建的pytorch环…...

TSINGSEE青犀视频汇聚平台EasyCVR多种视频流播放协议介绍

众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。今天我们来说一说,EasyCVR平台支持分…...

Vivado进行自定义IP封装

一. 简介 本篇文章将介绍如何使用Vivado来对上篇文章(FPGA驱动SPI屏幕)中的代码进行一个IP封装,Vivado自带的IP核应该都使用过,非常方便。 这里将其封装成IP核的目的主要是为了后续项目的调用,否则当我新建一个项目的时候,我需要将…...

开放自动化软件的硬件平台

自动化行业的产品主要以嵌入式系统为主,历来对产品硬件的可靠性和性能都提出很高的要求。最典型的产品要数PLC。PLC 要求满足体积小,实时性,可靠性,可扩展性强,环境要求高等特点。它们通常采用工业级高性能嵌入式SoC 实…...

AdvancedInstaller打包程序

文章目录 1. AdvancedInstaller 下载2. AdvancedInstaller 启动3. 新建工程4. 配置安装包详细信息5. 配置安装参数6. 添加要打包的文件7. 设置安装完成后启动程序8. 构建打包 1. AdvancedInstaller 下载 下载网址:https://www.advancedinstaller.com/ 2. AdvancedIn…...

无穷限积分习题

前置知识:无穷限积分 习题1 计算 ∫ 1 ∞ ln ⁡ x x 2 d x \int_1^{\infty}\dfrac{\ln x}{x^2}dx ∫1∞​x2lnx​dx 解: \qquad 原式 ( − ln ⁡ x x ) ∣ 1 ∞ ∫ 1 ∞ 1 x 2 d x ( − ln ⁡ x x ) ∣ 1 ∞ ( − 1 x ) ∣ 1 ∞ (-\dfrac{\…...

AI 3D结构光技术加持,小米引领智能门锁新标准

一直以来,小米智能门锁系列产品让更多家庭走进了安全便捷的智能生活,安全至上的设计让很多家庭都轻松告别了随身钥匙。 7月27日,小米正式推出小米智能门锁M20 Pro,再一次引领智能门锁产品的发展潮流。该款门锁采用AI 3D结构光技术…...

管理类联考——逻辑——形式逻辑——汇总篇

简述 形式逻辑: 识别题型:逻辑符号表达及标志词:联假言符号化特殊命题“除非否则”;五大关系:矛盾、等价、包含、至少有一真、至少有一假;【通过“关系”,串联起“假联选”言】 识别题型&…...

架构的分类

目录 一、 RUP41 架构 1.1 RUP41架构方法概述 1.2 RUP41架构总体 1.3 RUP41架构方法内容 1.3.1 逻辑视图 1.3.2 开发视图 1.3.3 物理视图 1.3.4 处理视图 1.3.5 场景视图 ​二、 TOGAF9 架构 2.1 TOGAF9 架构概述 2.2 TOGAF9 架构分类 2.2.1 业务架构 2.2.2 数据架…...

[SQL挖掘机] - 窗口函数 - lag

介绍: lag() 是一种常用的窗口函数,它用于获取某一行之前的行的值。它可以用来在结果集中的当前行之前访问指定列的值。 用法: lag() 函数的语法如下: lag(列名, 偏移量, 默认值) over (partition by 列名1, 列名2, ... order by 列名 [asc|desc], .…...

springboot项目如何自动重启(使用Devtools检测修改并自动重启springboot)

1. 问题: 我们在项目开发阶段,可能经常会修改代码,修改完后就要重启Spring Boot。经常手动停止再启动,比较麻烦。 所以我们引入一个Spring Boot提供的开发工具; 只要源码或配置文件发生修改,Spring Boot应用…...

docker: Error response from daemon: No command specified.

执行 docker run -it -d -v /home/dell/workspace/workspace/test_192.168.1.202_pipeline:/home/workspace1 --name test_192.168.1.202_pipeline_10 qnx:7.1报错 问题定位:export导入的镜像需要带上command,以下命令查看command信息 docker ps --no…...

百度地图点标记加调用

先看效果 PHP代码 <?phpnamespace kds_addons\edata\controller;use think\addons\Controller; use think\Db;class Maps extends Controller {// 经纬度计算面积function calculate_area($points){$totalArea 0;$numPoints count($points);if ($numPoints > 2) {f…...

MySQL 其他数据库日志

我们了解数据库事务时&#xff0c;知道两种日志&#xff1a;重做日志&#xff0c;回滚日志。 对于线上数据库应用系统&#xff0c;突然遭遇 数据库宕机 怎么办&#xff1f;在这种情况下&#xff0c;定位宕机的原因 就非常关键。我们可以查看数据库的 错误日志。因为日志中记录…...

为何企业和开发团队应该重视进行兼容性测试

随着科技的不断进步和软件的广泛应用&#xff0c;保证软件在不同平台和环境下正常运行变得至关重要。本文将探讨软件兼容性测试的重要性和好处&#xff0c;并介绍为何企业和开发团队应该重视进行兼容性测试&#xff0c;以确保软件的稳定性和用户体验。 提供用户友好的体验 软件…...

牛客网Verilog刷题——VL51

牛客网Verilog刷题——VL51 题目答案 题目 请编写一个十六进制计数器模块&#xff0c;计数器输出信号递增每次到达0&#xff0c;给出指示信号zero&#xff0c;当置位信号set 有效时&#xff0c;将当前输出置为输入的数值set_num。模块的接口信号图如下&#xff1a; 模块的时序图…...

从零实现深度学习框架——Transformer从菜鸟到高手(一)

引言 &#x1f4a1;本文为&#x1f517;[从零实现深度学习框架]系列文章内部限免文章&#xff0c;更多限免文章见 &#x1f517;专栏目录。 本着“凡我不能创造的&#xff0c;我就不能理解”的思想&#xff0c;系列文章会基于纯Python和NumPy从零创建自己的类PyTorch深度学习框…...

数组指针

数组指针的定义 1.数组指针是指针还是数组&#xff1f; 指针。 int a 10;int* p &a;//指向整型数据的指针 char b w;char* q &b;//指向字符变量的指针 所以数组指针应该是指向数组的指针。 2.数组指针应该怎么定义&#xff1f; int arr[10] { 0 };int(*p)[10] …...

C++设计模式之过滤器设计模式

C过滤器设计模式 什么是过滤器设计模式 过滤器设计模式是一种行为型设计模式&#xff0c;它允许你在特定的条件下对输入或输出进行过滤&#xff0c;以便实现不同的功能。 该模式有什么优缺点 优点 可扩展性&#xff1a;过滤器设计模式允许您轻松地添加、删除或替换过滤器&a…...

SpringBoot整合RedisTemplate操作Redis数据库详解(提供Gitee源码)

前言&#xff1a;简单分享一下我在实际开发当中如何使用SpringBoot操作Redis数据库的技术分享&#xff0c;完整的代码我都提供了出来&#xff0c;大家按需复制使用即可&#xff01; 目录 一、导入pom依赖 二、yml配置文件 三、使用FastJson序列化 四、核心配置类 五、工具…...

SQL 执行计划管理(SPM)

一、SPM 需求背景 任何数据库应用程序的性能在很大程度上都依赖于查询执行&#xff0c;尽管优化器无需用户干预就可以评估最佳计划&#xff0c;但是 SQL 语句的执行计划仍可能由于以下多种原因发生意外更改&#xff1a;版本升级、重新收集优化器统计信息、改变优化器参数或模式…...

浅谈微服务异步解决方案

导言 异步是一种设计思想&#xff0c;不是设计目的&#xff0c;因此不要为了异步而异步&#xff0c;要有所为&#xff0c;有所不为。 异步不是『银弹』&#xff0c; 避免试图套用一个『异步框架』解决所有问题&#xff0c; 需要根据不同的业务特点或要求&#xff0c;选择合适的…...

【音视频SDK测评】线上K歌软件开发技术选型

摘要 在线K歌软件的开发有许多技术难点&#xff0c;需考虑到音频录制和处理、实时音频传输和同步、音频压缩和解压缩、设备兼容性问题等技术难点外&#xff0c;此外&#xff0c;开发者还应关注音乐版权问题&#xff0c;确保开发的应用合规合法。 前言 前面写了几期关于直播 …...

Jackson:String转object反序列化失败

场景 消费mq时String转Object 代码 for (MessageExt msg : msgs) {String msgBody new String(msg.getBody(), StandardCharsets.UTF_8);BinlogEvent binlogEvent JsonUtil.silentString2Object(msgBody, BinlogEvent.class);binlogEvent.setPort(Long.valueOf(port));tCo…...

Spark_Core---6

spark 相关概念补充 课程目标 了解spark的安装部署知道spark作业提交集群的过程 6.1 spark的安装部署 1、下载spark安装包 http://spark.apache.org/downloads.html 高版本不存在cdh的编译版本&#xff0c;可以从官网下载源码版本&#xff0c;指定高版本hadoop进行编译 编译…...