【基于 JS 函数式编程-3】柯里化 | 偏函数 | 组合与管道

偏函数与柯里化:函数式编程技巧 如果你写过一段时间的代码,尤其是接触过函数式编程(Functional Programming),那么你一定听说过「柯里化」(Currying)和「偏函数」(Partial Application)这两个术语。它们听起来像是数学课本里的概念,但实际上,它们是日常开发中非常实用的技巧,能够让你的代码更灵活、更可复用、更易组合。 阅读详情

⭐️ 本文首发自 前端修罗场(点击即可加入),一个专注 Web 技术、答疑解惑、面试辅导、职业发展的社区。

相关文章

❤️ 现在关注【前端修罗场】,后台回复【666】,即可获取一份【免费的优质学习资料】,一起学习,一起进步~


如题,理解柯里化和偏应用,能帮助我们在函数式组合中进行应用。

概念

一元函数

定义: 只接受一个参数的函数,称为一元函数。如:

const fn = (x)=>x;

二元函数

定义:接受两个参数的函数,称为二元函数。如:

const add =(x,y)=>x+y;

变参函数

定义:接受可变数量参数的函数,称为变参函数。
在es5中我们可以通过arguments来捕获调用变参函数的额外参数。
在es6中,我们可以使用扩展运算符:"..."实现变参函数。如:

const varfn = (a,...varparms)=> {
	console.log(a);
	console.log(varparms);
}
varfn(1,2,3); // 1 , [2,3],我们把[2,3]称为额外参数

柯里化

定义:柯里化(Curry,以数学家Haskell Curry命名),常被翻译为“局部套用”,是把一个多参函数转换为一系列单参函数并进行调用的过程。

柯里化允许我们把函数与传递给这个函数的参数相结合,产生出一个新的函数。

如:下列代码中,add1是把1传递给add函数的curry方法后创建的一个新函数。

let add1 = add.curry(1);
console.log(add1(3));

再如:

const add =(x,y)=>x+y; //二元函数
进行柯里化:
const addCurry = x => y=>x+y;
addCurry(2)(3); //5

但是,Javascript自己并没有Curry方法。我们可以通过给Function.prototype扩展此功能:

Function.method('curry',function(){
	let slice = Array.prototype.slice,
		args = slice.apply(arguments),
		//arguments并非真正的数组,没有concat方法,要避开这个问题,
		//我们必须在两个arguments数组上应用数组的slice方法。
		//这样能产生出拥有concat方法的常规数组
		that = this;
		return function() {
			return that.apply(null,args.concat(slice.apply(arguments)));
		}
})

curry函数定义

const curry = (binaryFn)=> {
	return function(firstArg) {
		return function(secondArg){
			return binaryFn(firstArg,secondArg);
		};
	};
};
let autoCurriedAdd = curry(add) //通过curry函数把add函数转换为一个柯里化函数
autoCurriedAdd(2)(3); //5

但是,有人会问:柯里化有什么用处呢?

因为有时候我们可能想把多个函数及带有多个参数的函数柯里化,所以,下面我们重构一下curry函数:

let curry = (fn)=> {
	if(typeof fn!=='function') {
		throw Error('No function provided!');
	}
	return function curriedFn(...args) {
		if(args.length<fn.length){ //检查通过...args传入的参数长度是否小于函数参数列表的长度。如果是,进入if,否则调用整个函数。
			return function() {
				return curriedFn.apply(null,args.concat([].slice.call(arguments)));//使用concat函数连接一次传入一个的参数,并递归调用curriedFn。
				//除此之外,由于args是类数组,并没有concat方法,
				//所以,需要应用数组的slice方法。
			};
		}
		return fn.apply(null,args);//直接调用整个函数
	};
};
const multiply = (x,y,z) =>x*y*z;
curry(multiply)(3)(2)(1);//6

偏应用

偏函数(partial)

const partial = function(fn,...partialArgs) {
	let args = partialArgs;//捕获传入函数的参数 args= [undefined,10]
	return function(...fullArguments) {//闭包函数,接受一个fullArguments的参数
	//fullArguments指向 console.log('1');
		let arg = 0;
		for(let i = 0;i < args.length&& arg<fullArguments.length;i++) {
			if(args[i] === undefined) {
				args[i] = fullArguments[arg++];
			}
		}
		return fn.apply(null,args);
	};
};
let delayTenMs = partial(setTimeout,undefined,10);
delayTenMs(()=>console.log('1'));

我们可以将partial函数应用于任何含有多个参数的函数。如:

let obj = {foo:"xxx",bar:"yyy"};
JSON.stringify(obj,null,2);
转换为应用偏函数:
let prettyPrintJson = partial(JSON.stringify,undefined,2);
prettyPrintJson({foo:"xxx",bar:"yyy"});//"{"foo":"xxx","bar":"yyy"}"

上面我们说了柯里化和偏函数,但是需要注意的是:

柯里化和偏函数并不是同时需要。这主要取决于API是如何定义的。如果API如,map、filter一样定义,我们可以使用curry函数解决问题。但是,如果不是为curry函数设计的函数,如setTimeout,有时填充函数的前两个参数和最后一个参数会使中间的参数处于一种未知状态(undefined)!我们选择partial更合适!

组合与管道

概念

在Unix中有这么一套思想:
1、每个程序只做好一件事情。为了完成一项新的任务,重新构建要好于在复杂的旧程序中添加新”属性“。在函数式编程中,”接受一个参数并返回数据“正是遵循了该条思路。
2、每个程序的输出应该是另一个尚未可知的程序的输入。

管道

管道允许我们通过组合一些函数去创建一个能够解决问题的新函数。

如图:

在这里插入图片描述

管道在两个函数之间扮演了桥梁的角色。

函数式组合

如下示例代码:

map(filter(arg,(item)=>item.rating[0]>4.5),(item)=>{
	return {}
})

我们看到,上面代码中filter输出的数据被作为输入参数传递给map函数。
这种创建一个函数,通过把一个函数的输出作为输入发送给另一个函数的方式把两个函数组合起来,我们称为函数式组合。组合的思想,就是把小函数组合成一个大函数。
示例:

//compose函数
const compose =(a,b)=> {
	(c)=>a(b(c)) //b的输出作为a的输入
}
let number = compose(Math.round,parseFloat);
number("3.56");//4

compose函数会首先执行b,并将b的返回值作为参数传递给a。该函数调用的方向是从右至左的,即先执行b,再执行a。

管道/序列

从左至右处理数据流的过程称为管道(pipeline)或序列。

//pipe函数,compose函数的复制品,修改了数据流
const pipe = (...fns) => 
	(value) => 
		reduce(fns,(acc,fn)=>fn(acc),value);

在这里插入图片描述

函数柯里化有什么用?JavaScript函数式编程实战与优化思路 函数柯里化,得名于逻辑学家哈斯凯尔·柯里(Haskell Curry),是指将一个接收多个参数的函数,转换成一系列只接收一个参数的函数的技术。每一次调用都只接收一个参数,并返回一个新的函数,直到接收到所有参数后才执行原始函数。用更具体的方式来描述:如果有一个函数f,它接收n个参数,即。经过柯里化后,它会变成的形式。每次调用都返回一个新的函数,直到所有参数都被接收。让我们通过一个简单的例子来理解这个概念。假设我们有一个简单的add函数,它接收两个数字并返回它们的和。// 输出: 5。 阅读详情

相关推荐

python自动交易 缠论_缠论自动交易系统实现了

一、缘起:缠中说禅的伟大,我想你只要上网搜索一下就知道了,他的才气不用我说,我只想说说他的《教你炒股票》系列文章,108篇文章,将他的股票操盘秘籍全部公开给广大的股友了,更有缠友将其理论写成公式,在网上也是广为流传,深受广大缠友们的喜爱。我也是缠中说禅的忠实崇拜者之一,从2008年开始每天都使用缠论来看盘,特别想将缠论写成交易系统,让计算加来帮助我们自动交易,从而减轻我们盯盘的辛劳,可是苦于没有好...

weixin_39970689的博客 4637

前端 JS 经典:函数管道

什么是函数管道,就是将多个函数组合起来形成一个新的函数,在函数管道中,前一个函数的输出,是后一个函数的输入。

weixin_64684095的博客 434

Qlib+backtrader:2014.1.1-2023.9.20最新回测结果,可以实盘吗?

今年以来,在研究了qlib和backtrader的基础上,把二者结合起来进行了一个策略研究。简单说就是用qlib在200只股票的股票池中进行滚动训练与预测(walk forward),总体数据范围是2005到2023年,以20日间隔滚动训练和预测,最终得到2014.1.1-2023.9.20共10年测试期的收益率预测结果。将预测结果代入backtrader进行回测,得到如下回测结果。

qtbgo的博客 1783

对于一个简单实现函数管道案例的讲解与拓展

这篇文章是关于 JavaScript 中一些高级特性的讨论。具体而言,我们讨论了一个使用函数管道语法的函数调用示例,并以此为基础进行了几种替代调用语法的实现。我们还涉及了不同类型的 JavaScript 函数定义(箭头函数和普通函数),以及在函数管道中使用展开运算符和 apply 方法的技术。虽然函数管道语法可以使代码更加直观和易读,但它仍然是 ES2021 中的实验性特性,应谨慎使用以确保代码的可靠性和兼容性。

qq_52554520的博客 334

在Qt中集成VTK和PCL:实现数据可视化的完整指南

在现代软件开发中,数据可视化至关重要。将VTK(Visualization Toolkit)和PCL(Point Cloud Library)集成到Qt环境里,能为开发者提供强大的可视化解决方案。本文将深入讲解如何在Qt中读取PCL文件,展示VTK模型和PCL数据,适合想要在Qt项目中实现数据可视化的开发者。

轩宇的博客 917

详细指南:如何在TI C2000基于C28x的微控制器上部署FreeRTOS_C语言实现

FreeRTOS是一个小型、开源、实时的嵌入式操作系统内核。它为多种微控制器和处理器提供了支持,包括ARM、AVR、MSP430和PIC等。小型和轻量级:适合资源受限的嵌入式系统。实时性:可以满足严格的实时要求。多任务支持:支持多任务并发执行。内存管理:提供了多种内存管理策略。模块化设计:用户可以根据需要选择所需的功能模块。TI C2000系列微控制器是专为实时控制应用设计的。高性能C28x核:提供了高速的浮点运算能力。丰富的外设:包括PWM、ADC、DAC、通信接口等。低功耗。

qq_38334677的博客 1606

JavaScript进阶教程 - 组合和管道

函数组合和管道提供了一种强大的方式来构建复杂的功能,通过将简单的函数链接在一起。这种方法提高了代码的可读性和可维护性,并鼓励了函数式编程的不可变和声明式原则。通过使用这些技术,开发者可以创建更清晰、更模块化的代码,更容易理解和维护。

r081r096的博客 1146

那些年我们create generate clock遇到的坑

想必大家对create_generate_clock这个命令熟悉的不能再熟悉了,然而,在真正做实际项目的时候,会出现各式各样的问题,比如在report timing时,generate clock路径不是我们预想的,source latency丢失等等,今天小编就跟大家重点聊聊source latency丢失的情况。clock时,首先我们要对一个项目的时钟结构要比较清晰,同时针对一些复杂的生成时钟,我们也要学会画波形图进行分析;接下来,我们就来看看哪些case会导致source latency丢失。

芯冰乐 136

JS中的pipe原理

学习reduce()时遇到一个练习“功能型函数管道”,对于代码中的pipe不能理解,类似于下面这一行代码,信息量很丰富,有es6中的扩展运算符,箭头函数,reduce()方法。 const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); 展开: const pipe = function(x, ...fns) { ...

qq_40664253的博客 5501

Oracle11g下载与安装(windows)

一、Oracle11g下载 二、安装准备 三、执行安装步骤 四、测试连接 附:Oracle11g服务说明

欢迎订阅我的“JAVA全栈开发笔记(全)”专栏,价格优惠,仅需19.9元。 2万+

【数字孪生与仿真技术】05:从原理到实战:MATLAB/Simulink在航空航天GNC系统开发中的深度解析(含全流程代码与认证指南)

摘要:本文系统阐述MATLAB/Simulink在航空航天制导、导航与控制(GNC)系统开发中的核心应用。通过解析GNC系统高保真动力学建模、多源导航传感器融合、鲁棒控制算法设计等关键技术原理,结合Virgin Orbit运载火箭、CubeSat等真实案例,展示从需求分析、模型搭建、仿真验证到代码生成与认证的完整开发流程。文中提供1200余行关键代码、8个可复用Simulink模型及详细执行结果展示,涵盖DO-178C认证支持、实时性能优化等工业级开发规范,为航空航天工程师、科研人员提供可落地的参考方案。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 1656

管道函数

管道函数简单的说就是执行一些函数,前一个函数的返回值作为下一个函数的参数以此类推,有些时候使用管道函数的思想可以帮我们简化代码。就像vue中的filter,其实就是利用管道函数的思想,使用管道符号‘|’分隔多个filter函数,前一个filter函数的返回值作为下一个filter函数的参数,最后所有filter函数执行完毕之后作为最终的过滤结果。 然后我也自己去尝试着写了一下管道函数的实现: function pipeFunc(...funcs) { return function (...arg

weixin_45638974的博客 1953

SDR 和 DDR 的区别

SDR 和 DDR 的差别,最明显提现在传输速率和频率上。DDR SDRAM与SDRAM的基本结构是相似的,最根本的区别在于DDR 支持在一个时钟周期内传输两次数据,,SDR 只能传输一次数据。

串起点滴 1960

33-js-concepts函数式编程:纯函数与函数组合技术

33-js-concepts函数式编程:纯函数与函数组合技术 【免费下载链接】33-js-concepts ???? 33 JavaScript concepts every developer should know. 项目地址: h...

gitblog_01162的博客 899

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

带你快速了解js的函数式编程

几个概念 函数式编程 函数式编程是一种范式,我们能够以此创建仅依赖输入就可以完成自身逻辑的函数。这保证了当函数被多次调用时仍然返回相同的结果。函数不会改变任何外部环境的变量,这将产生可缓存的、可测试的代码库。 引用透明性 引用透明性是指所有的函数对于相同的输入都将返回相同的值。 那么在调用这个函数的时候,我们不必关心函数内部的实现,只用结果就好。你可以储存这个结果,甚至直接用结果去替换函数的调用。 命令式和声明式 命令式就是告诉编译器如何做。 比如,遍历一个数组: const array = [1, 2,

BIGDgreen 991

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

终极JavaScript函数式编程库Lemonad:源自Clojure与Haskell的一次优雅实验

如果你正在寻找一款既轻量又能让你真正理解**JavaScript函数式编程**精髓的库,那么 Lemonad 值得你花十分钟了解。这个由 Fogus 发起的开源项目,是一个受 **Clojure、Haskell、SML 与 Forth** 启发而生的 JavaScript 函数式编程库,作者称它为"一次优雅的实验"。它没有庞大复杂的 API 体系,而是把柯里化、偏函数应用、管道组合、引用与单子等函

gitblog_00896的博客 414

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

JavaScript函数式编程实战指南:从纯函数到数据管道

函数式编程是一种以函数为基本单元、通过组合与数据流来构建程序的编程范式。其核心原理是让函数保持纯函数特性——相同输入始终得到相同输出,且不产生外部副作用,配合不可变数据来提升代码的确定性与可预测性。这种范式在工程实践中的价值在于:让复杂逻辑可拆解为独立小函数,便于单独测试与复用,同时显著降低因共享状态引发的维护成本。在JavaScript领域,函数式编程广泛应用于前端状态管理(如Redux、React Hooks)、Node.js后端的数据转换管道,以及异步流程的组合与错误处理。掌握map、filter、r

weixin_30443075的博客 387

JavaScript柯里化与偏函数应用:从闭包原理到API客户端实战

柯里化(Currying)和偏函数应用(Partial Application)是函数式编程中提升代码可维护性的核心机制,其本质基于JavaScript闭包实现参数预绑定与函数形态转换。它们通过将多参函数拆解为单参链式调用或固定部分参数生成新函数,显著增强组合性、可测试性与复用性。在工程实践中,二者广泛应用于API客户端封装、React事件处理器简化、Promise异步流控制等场景,配合Ramda等库可规避this绑定风险、提升TypeScript类型推导精度。本文聚焦于从原生闭包原理出发,逐步构建生产级可

weixin_34198762的博客 330

2024年最全函数式编程总结(1),2024年最新Web前端事件体系全面总结+实践分析

打开全栈工匠技能包-1小时轻松掌握SSR两小时精通jq+bs插件开发生产环境下如歌部署Node.js网易内部VUE自定义插件库NPM集成谁说前端不用懂安全,XSS跨站脚本的危害webpack的loader到底是什么样的?两小时带你写一个自己loader// ES5}`具体可以参考。

2301_82244330的博客 1027

《JavaScript ES6 函数式编程入门经典》--推荐指数⭐⭐⭐

这本书比较基础认真看完再自己写点demo一个双休日就差不多, 总体来说看完还是有收获的,会激起一些你对函数编程的兴趣 主要目录如下: 第1章 函数式编程简介 11.1 什么是函数式编程?为何它重要 11.2 引用透明性 41.3 命令式、声明式与抽象 51.4 函数式编程的好处 71.5 纯函数 71.5.1 纯函数产生可测试的代码 71.5.2 合理的代码 91.6 并...

weixin_30542079的博客 318

深入理解 jstips 第 28 期:柯里化(Currying)与偏函数应用(Partial Application)的区别与实践

柯里化与偏函数应用是函数式编程中两种最常见的"降元"手段:它们都能把接收多个参数的函数改造成元数(arity)更小的函数,但改造方式与使用场景截然不同。本文以 jstips 仓库 Tip 28([原始文档](https://link.gitcode.com/i/78bd14e6b0f614e26235636c2c3bbd1d))为核心骨架,通过类型签名、算法与可运行的 JavaScript 代码,

gitblog_00459的博客 213

JavaScript函数式编程工程实践:用纯函数、组合与不可变数据简化逻辑

函数式编程作为现代 JavaScript 工程中的关键方法论,其核心价值在于利用纯函数、不可变数据和函数组合来构建可预测、易测试的代码逻辑。本文将深入剖析这些基础概念的实现原理,探讨如何通过高阶函数如 map、filter、reduce 构建清晰的数据处理管道,并利用函数组合(pipe/compose)与副作用隔离降低复杂业务场景的认知负担。文章不仅对比了 Haskell、Java 8 的函数式特性差异,还结合 Maybe/Either 模式处理空值与错误分支,最终通过一个订单数据转换模块完整演示了函数式思

weixin_30437847的博客 402

函数式编程总结

面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。需要完整面试题的朋友可以点击蓝色字体获取分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。需要完整面试题的朋友可以点击蓝色字体获取。

2401_84094725的博客 364

什么是函数式编程

关于函数式编程,我们可以参考数学中的函数定义f(X) = Y这段语句可以被解释为:一个函数f接收一个参数X,并根据X计算返回一个结果Y。这样的函数被称为一元函数,即接收一个参数的函数,相应的接收两个参数的函数被称为二元函数;函数式编程借鉴了大量数学函数的思想,通过数学函数的定义我们能够窥见函数式编程的本质。函数必须接收一个参数函数必须返回一个值函数应该基于给定的参数运行,不能依赖于外部环境对于相同的参数X,总是返回相同的结果Y看完数学函数的定义,我们来看看JavaScript中的函数。

yuanfgbb的博客 161

javascript函数式程

什么是函数式编程 基于自己的理解,函数式编程与面向对象编程一样是一种编程思想,它不是用函数编程,主旨在于将复杂的函数合成简单的函数,它与数理逻辑、微积分、行列式是同一类东西,都是数学方法,只是碰巧它能用来写程序。它是随着React高阶函数逐渐火起来的 函数式编程的特点 函数是一等公民。所谓“一等公民”,指的是函数与其它类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或...

W_wjl1900的专栏 332

说说函数式编程

说说 函数式编程

weixin_42317878的博客 216
上一篇: 【基于 JS 函数式编程 - 2】高阶函数 | 函数代替数据传递 | 函数是一等公民 | 闭包 | 使用高阶函数实现抽象 | 数组的高阶函数
下一篇: 【基于 JS 的函数式编程 - 4】函子 | MayBe函子 | Monad函子
蟹蟹蟹风流
蟹蟹蟹风流 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域 领域专家: 前端开发技术领域 领域专家: 前端开发技术领域
博客等级 码龄11年 3万+粉丝 396原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蟹蟹蟹风流

期望和你分享一杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值