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

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
应用场景
- 参数复用
柯里化可以让我们轻松复用参数。例如:
function discounts(price, discount) {return price * discount;
}// 柯里化后
const tenPercentDiscount = discounts(0.1);
tenPercentDiscount(500); // 50
tenPercentDiscount(200); // 20
- 提前返回函数副本
有时我们需要提前返回函数的副本给其他模块使用,这时可以用柯里化。
// 模块A
function ajax(type, url, data) {// 发送ajax请求
}// 柯里化后
export const getJSON = curry(ajax)('GET');// 模块B
import { getJSON } from './moduleA'; getJSON('/users', {name: 'John'});
- 延迟执行
柯里化函数在调用时并不会立即执行,而是返回一个函数等待完整的参数后再执行。这让我们可以更加灵活地控制函数的执行时机。
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 并传入参数,Until 把args所有参数都传给 fn。
利用这个模式可以轻松实现反柯里化:
const curriedAdd = a => b => a + b;const uncurriedAdd = uncurry(curriedAdd);
uncurriedAdd(1, 2); // 3
应用场景
- 统一接口规范
有时我们会从其他模块接收到一个柯里化的函数,但我们的接口需要一个普通的多参数函数。这时可以通过反柯里化来实现统一。
// 模块A导出
export const curriedGetUser = id => callback => {// 调用callback(user)
};// 模块B中
import { curriedGetUser } from './moduleA';// 反柯里化以符合接口
const getUser = uncurry(curriedGetUser); getUser(123, user => {// use user
});
- 提高参数灵活性
反柯里化可以让我们以任意顺序 passes 入参数,增加了函数的灵活性。
const uncurriedLog = uncurry(console.log);uncurriedLog('a', 'b');
uncurriedLog('b', 'a'); // 参数顺序灵活
- 支持默认参数
柯里化函数不容易实现默认参数,而反柯里化后可以方便地设置默认参数。
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∞x2lnxdx 解: \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 其他数据库日志
我们了解数据库事务时,知道两种日志:重做日志,回滚日志。 对于线上数据库应用系统,突然遭遇 数据库宕机 怎么办?在这种情况下,定位宕机的原因 就非常关键。我们可以查看数据库的 错误日志。因为日志中记录…...
为何企业和开发团队应该重视进行兼容性测试
随着科技的不断进步和软件的广泛应用,保证软件在不同平台和环境下正常运行变得至关重要。本文将探讨软件兼容性测试的重要性和好处,并介绍为何企业和开发团队应该重视进行兼容性测试,以确保软件的稳定性和用户体验。 提供用户友好的体验 软件…...
Context Engineering 实战:别再往 context 里塞东西了
Context Engineering 实战:别再往 context 里塞东西了 为什么 token 塞满反而让 LLM 变蠢?四种核心策略 Python 代码实现 Agent 跑到第 15 步,突然开始做蠢事。 它把已经检查过的文件又检查了一遍,给出了和第 3 步完全矛盾的结论…...
OpCore-Simplify:开源系统硬件适配的自动化配置引擎
OpCore-Simplify:开源系统硬件适配的自动化配置引擎 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 在跨平台系统部署领域,硬件…...
新手入门指南,五分钟完成Taotoken账号注册与第一个API调用
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 新手入门指南,五分钟完成Taotoken账号注册与第一个API调用 对于初次接触大模型API的开发者来说,如何快速上…...
三步解锁:开源AI编程工具的免费共享方案
三步解锁:开源AI编程工具的免费共享方案 【免费下载链接】cursor-vip cursor IDE enjoy VIP 项目地址: https://gitcode.com/gh_mirrors/cu/cursor-vip 在当今AI编程工具费用日益高涨的背景下,cursor-vip项目为全球开发者提供了一个创新的解决方案…...
别再新建空文件了!手把手教你用CodeBlocks创建可调试的C/C++工程(避坑中文路径)
别再新建空文件了!手把手教你用CodeBlocks创建可调试的C/C工程(避坑中文路径) 刚接触编程的新手常常会遇到这样的困惑:明明按照教程写好了代码,设置了断点,按下F7却毫无反应。这种挫败感往往源于一个被多数…...
如何快速实现无人机合规飞行:基于ESP32的完整远程识别解决方案
如何快速实现无人机合规飞行:基于ESP32的完整远程识别解决方案 【免费下载链接】ArduRemoteID RemoteID support using OpenDroneID 项目地址: https://gitcode.com/gh_mirrors/ar/ArduRemoteID 在FAA和欧盟无人机法规日益严格的背景下,远程识别已…...
ComfyUI InstantID终极指南:5分钟掌握AI人像风格化核心技术
ComfyUI InstantID终极指南:5分钟掌握AI人像风格化核心技术 【免费下载链接】ComfyUI_InstantID 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI_InstantID 你是否曾经想过,如何将自己或朋友的照片变成一幅精美的艺术作品,同时…...
收藏必备!小白程序员快速入门RAG,解锁大模型知识检索与增强(干货满满)
本文详细介绍了RAG(检索增强生成)的概念、流程及优化策略。RAG通过从数据库检索上下文文档,有效提升LLM答案的准确性与时效性,解决纯生成模型的局限性。文章覆盖了文档加载、切分、向量化存储,以及检索与生成两个核心阶…...
比特币钱包密码恢复神器:如何用btcrecover找回遗忘的数字资产密码
比特币钱包密码恢复神器:如何用btcrecover找回遗忘的数字资产密码 【免费下载链接】btcrecover An open source Bitcoin wallet password and seed recovery tool designed for the case where you already know most of your password/seed, but need assistance i…...
macOS完整安装器下载工具终极指南:轻松获取Big Sur系统安装包
macOS完整安装器下载工具终极指南:轻松获取Big Sur系统安装包 【免费下载链接】DownloadFullInstaller macOS application written in SwiftUI that downloads installer pkgs for the Install macOS Big Sur application. 项目地址: https://gitcode.com/gh_mirr…...
