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

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 其他数据库日志
我们了解数据库事务时,知道两种日志:重做日志,回滚日志。 对于线上数据库应用系统,突然遭遇 数据库宕机 怎么办?在这种情况下,定位宕机的原因 就非常关键。我们可以查看数据库的 错误日志。因为日志中记录…...
为何企业和开发团队应该重视进行兼容性测试
随着科技的不断进步和软件的广泛应用,保证软件在不同平台和环境下正常运行变得至关重要。本文将探讨软件兼容性测试的重要性和好处,并介绍为何企业和开发团队应该重视进行兼容性测试,以确保软件的稳定性和用户体验。 提供用户友好的体验 软件…...
RMBG-2.0从零开始:Ubuntu 22.04 + CUDA 12.1完整环境搭建
RMBG-2.0从零开始:Ubuntu 22.04 CUDA 12.1完整环境搭建 想体验一键抠图,把照片背景变得干干净净?今天,我们就来手把手教你,在Ubuntu 22.04系统上,从零开始搭建一个基于RMBG-2.0模型的智能抠图环境。RMBG-…...
ChatGPT AI生成式引擎优化*(GEO)方案
ChatGPT AI生成式引擎优化*(GEO)方案: 技术支持:拓世网络技术开发工作室 1️⃣ 战略规划阶段 目标明确 内容类型 文本:文章、产品描述、文案、技术文档 图像:营销图片、社交媒体图、设计草图 视频/动画&…...
OpenClaw技能扩展实战:用Phi-3-vision自动生成图文周报
OpenClaw技能扩展实战:用Phi-3-vision自动生成图文周报 1. 为什么需要自动化周报 每周五下午,我的电脑桌面上总会堆满散乱的Excel表格、截图和零散的Markdown笔记。作为技术负责人,我需要将这些碎片信息整合成结构化的图文周报,…...
Windows 上路由、端口转发配置
一、背景 有时候我们会遇到这样的场景,一批同一局域网中只有某一台主机带外且系统为windows,局域网中其他非带外的主机多是Linux,他们想要访问外网或外网连入管理,又不想新增公网资产增加成本,基于此,本文将介绍如何配置在带外主机上开启路由及端口转发。 关联资源:网络…...
终极指南:如何将Python PEG语法轻松迁移到Ohm解析器
终极指南:如何将Python PEG语法轻松迁移到Ohm解析器 【免费下载链接】ohm A library and language for building parsers, interpreters, compilers, etc. 项目地址: https://gitcode.com/gh_mirrors/oh/ohm 想要将Python的PEG语法迁移到Ohm解析器框架吗&…...
GHelper硬件控制工具:华硕笔记本性能优化与系统管理完全指南
GHelper硬件控制工具:华硕笔记本性能优化与系统管理完全指南 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus, Flow, TUF, Str…...
Fish-Speech-1.5与LLM集成:构建智能对话系统的完整指南
Fish-Speech-1.5与LLM集成:构建智能对话系统的完整指南 1. 引言 想象一下,你正在开发一个智能客服系统,用户用语音提问,系统不仅能理解问题,还能用自然流畅的语音回答。这听起来像是科幻电影里的场景,但现…...
Claude Code 宠物彩蛋来袭:/buddy 完整玩法指南(整理了宠物刷取方法,重置并刷到你想要的宠物)
文章目录 📖 介绍 📖 🏡 演示环境 🏡 📒 Claude Code /buddy 宠物指南 📒 📝 初识 Buddy 🎯 原理解析 🎯 预热窗口期 📝 如何触发 Buddy 🐙 18种宠物图鉴:你的伙伴是哪一位 📝 稀有度系统:1%传说级的诱惑 📝 五维属性:你的宠物是什么性格 📝 成…...
鸿蒙开发实战:HDC工具在本地模拟器中的高效调试技巧
1. HDC工具入门:鸿蒙开发的瑞士军刀 第一次接触HDC工具时,我把它当成了鸿蒙版的ADB。但用久了才发现,这个看似简单的命令行工具,其实是鸿蒙开发的万能钥匙。HDC全称Huawei Device Connector,就像它的名字一样ÿ…...
企业级“衣依”服装销售平台管理系统源码|SpringBoot+Vue+MyBatis架构+MySQL数据库【完整版】
💡实话实说:有自己的项目库存,不需要找别人拿货再加价,所以能给到超低价格。摘要 随着电子商务的快速发展,服装行业对高效、智能化的销售管理平台需求日益增长。传统的线下销售模式在库存管理、订单处理及客户服务等方…...
