函数柯里化

前端面试大全·JavaScript函数柯里化

🌟经典真题

🌟什么是函数柯里化

🌟柯里化快速入门

🌟函数柯里化实际应用

🌟封装通用柯里化函数

🌟一道经典的柯里化面试题

🌟真题详解

🌟总结


🌟经典真题

  • 什么是函数柯里化?

🌟什么是函数柯里化

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是 Moses Schönfinkel 和戈特洛布·弗雷格发明的。

在直觉上,柯里化声称如果你固定某些参数,你将得到接受余下参数的一个函数。

我们姑且叫它返回函数,在调用返回函数的时候,它将判断当前的参数和之前被柯里化函数固定的参数拼起来之后,是否达到了原本函数的参数个数。

如果是,则执行原本的函数,得到结果;如果没有达到,则要继续调用柯里化函数来固定目前的参数。

在理论计算机科学中,柯里化提供了在简单的理论模型中,比如:只接受一个单一参数的 lambda 演算中,研究带有多个参数的函数的方式。

函数柯里化的对偶是Uncurrying,一种使用匿名单参数函数来实现多参数函数的方法。

🌟柯里化快速入门

接下来,我们来通过一个简单的示例,让大家快速体会函数柯里化。

假设我们有一个求取两个数之和的函数:

function add(x, y) {return x + y;
}
console.log(add(1, 2)); // 3
console.log(add(5, 7)); // 12

在上面的示例中,我们有一个 add 函数,接收两个形参,返回两形参的和。

在调用的时候,我们每次也需要传递两个参数。

现在,我们对其进行柯里化,如下:

function add(x) {return function (y) {return x + y;}
}
console.log(add(1)(2)); // 3
console.log(add(5)(7)); // 3

在上面的代码中,我们对 add 函数进行了柯里化改造,只接受一个参数,但是返回的也不是值了,而是返回一个函数,这个函数也接收一个参数,然后利用闭包的特性,可以访问到最开始传入的 x 的值,最终返回 x 和 y 的和。

所以,通过上面的这个示例,我们能够体会到前面所说的柯里化函数的特点:

一个柯里化的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

🌟函数柯里化实际应用

通过上面的例子,我们体验到了什么是柯里化函数。

但是问题来了,费这么大劲封装一层,到底有什么用处呢?

没有好处想让我们程序员多干事情是不可能滴,这辈子都不可能。

所以接下来我们就来看一下函数柯里化的一个实际应用。

参数复用

就是将相同的参数固定下来。

// 正常正则验证字符串 reg.test(txt)// 函数封装后
function check(reg, txt) {return reg.test(txt)
}// 即使是相同的正则表达式,也需要重新传递一次
console.log(check(/\d+/g, 'test1')); // true
console.log(check(/\d+/g, 'testtest')); // false
console.log(check(/[a-z]+/g, 'test')); // true// Currying后
function curryingCheck(reg) {return function (txt) {return reg.test(txt)}
}// 正则表达式通过闭包保存了起来
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)console.log(hasNumber('test1')); // true
console.log(hasNumber('testtest'));  // false
console.log(hasLetter('21212')); // false

上面的示例是一个正则的校验,正常来说直接调用 check 函数就可以了,但是如果我有很多地方都要校验是否有数字,其实就是需要将第一个参数 reg 进行复用,这样别的地方就能够直接调用 hasNumber、hasLetter 等函数,让参数能够复用,调用起来也更方便。

提前确认

/*** * @param {要绑定事件的 DOM 元素} element * @param {绑定什么事件} event * @param {事件处理函数} handler */
var on = function (element, event, handler) {if (document.addEventListener) {if (element && event && handler) {element.addEventListener(event, handler, false);}} else {if (element && event && handler) {element.attachEvent('on' + event, handler);}}
}on(div, 'click', function(){})var on = (function () {if (document.addEventListener) {return function (element, event, handler) {if (element && event && handler) {element.addEventListener(event, handler, false);}};} else {return function (element, event, handler) {if (element && event && handler) {element.attachEvent('on' + event, handler);}};}
})();on(div, 'click', function(){})//换一种写法可能比较好理解一点,上面就是把 isSupport 这个参数给先确定下来了
var on = function (isSupport, element, event, handler) {isSupport = isSupport || document.addEventListener;if (isSupport) {return element.addEventListener(event, handler, false);} else {return element.attachEvent('on' + event, handler);}
}
on(true, div, 'click', function(){})
on(true, div, 'click', function(){})
on(true, div, 'click', function(){})

我们在做项目的过程中,封装一些 DOM 操作可以说再常见不过,上面第一种写法也是比较常见,但是我们看看第二种写法,它相对于第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。

🌟封装通用柯里化函数

接下来我们来封装一个通用的柯里化函数。

function curry() {var fn = arguments[0]; // 获取要执行的函数var args = [].slice.call(arguments, 1); // 获取传递的参数,构成一个参数数组// 如果传递的参数已经等于执行函数所需的参数数量if (args.length === fn.length) {return fn.apply(this, args)}// 参数不够向外界返回的函数function _curry(){// 推入之前判断// 将新接收到的参数推入到参数数组中args.push(...arguments);if(args.length === fn.length){return fn.apply(this, args)}return _curry;}return _curry;
}

对上面的代码进行测试:

// 测试 1
function add(a, b, c) {return a + b + c;
}console.log(curry(add)(1)(2)(3)); // 6
console.log(curry(add, 1)(2)(3)); // 6
console.log(curry(add, 1, 2, 3)); // 6
console.log(curry(add, 1)(3, 4)); // 8var addCurrying = curry(add)(2);
console.log(addCurrying(7)(8)); // 17// 测试 2
function check(reg, txt) {return reg.test(txt)
}
var hasNumber = curry(check)(/\d+/g);
console.log(hasNumber('test1'));// true

🌟一道经典的柯里化面试题

实现一个 add 方法,使计算结果能够满足如下预期:

add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

要完成上面的需求,我们就可以使用柯里化函数:

function add() {// 第一次执行时,定义一个数组专门用来存储所有的参数var _args = Array.prototype.slice.call(arguments);// 在内部声明一个函数,利用闭包的特性保存 _args 并收集所有的参数值var _adder = function () {_args.push(...arguments);return _adder;};// 这个是最后输出的时候被调用的,return 后面如果是函数体,// 为了输出函数体字符串会自动调用 toString 方法// 利用 toString 隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回_adder.toString = function () {return _args.reduce(function (a, b) {return a + b;});}// 这个 return 是第一次调用的时候返回上面的函数体,// 这样后面所有的括号再执行的时候就是执行 _adder 函数体return _adder;
}
console.log(add(1)(2)(3).toString()); // 6
console.log(add(1, 2, 3)(4).toString()); // 10
console.log(add(1)(2)(3)(4)(5).toString()); // 15
console.log(add(2, 6)(1).toString()); // 9

🌟真题详解

  • 什么是函数柯里化?

参考答案:

柯里化(currying)又称部分求值。一个柯里化的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

举个例子,就是把原本:

function(arg1,arg2) 变成 function(arg1)(arg2) function(arg1,arg2,arg3) 变成 function(arg1)(arg2)(arg3) function(arg1,arg2,arg3,arg4) 变成 function(arg1)(arg2)(arg3)(arg4)

总而言之,就是将:

function(arg1,arg2,…,argn) 变成 function(arg1)(arg2)…(argn)

🌟总结

本篇文章是关于JavaScript的一道面试题,后续还会持续更新HTML、CSS、JavaScript、Node.js、Vue.js、网络等前端相关面试题。如果文中出现有瑕疵的地方各位通过评论或者私信联系我,我们一起进步,有兴趣的伙伴可以关注订阅: 前端面试题大全    

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/193140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言结构体详解(二)(能看懂文字就能明白系列)文章很长,慢慢品尝

系列文章目录 第一章 结构体的介绍和基本使用 🌟 个人主页:古德猫宁- 🌈 信念如阳光,照亮前行的每一步 文章目录 系列文章目录🌈 *信念如阳光,照亮前行的每一步* 前言前面一篇文章主要介绍了结构体的基础…

C语言-结构体

---------------------------- ------------------ 岁月漫长心怀热爱,携手共赴星辰大海 --------今天来到我们自定义类型 -----结构体的讲解 目录 结构体的类型声明和初始化 结构体的类型声明 结构体成员的直接访问 结构体成员的间接访问 嵌套结构体进行访问 使用…

基于搜索协议实现工业设备升级

目录 1、背景引入 2、技术分析 3、过程概述 4、服务器端流程 5、客户端流程 6、效果展示 7、源码 7.1 master(主控) 7.2 device(设备) 8、注意事项 1、背景引入 在工业生产中,设备的升级和维护是非常重要的…

一起学docker系列之十六使用Docker Compose简化容器编排

目录 1 前言2 Docker Compose是什么?3 Docker Compose安装步骤3.1 **下载Compose**3.2 **设置权限**3.3 **创建符号链接(可选但建议以便使用)** 4 Docker Compose的核心概念4.1 **YAML文件(docker-compose.yml)**4.2 *…

SpringBootCache缓存——j2cache

文章目录 缓存供应商变更&#xff1a;j2cache 缓存供应商变更&#xff1a;j2cache <!-- https://mvnrepository.com/artifact/net.oschina.j2cache/j2cache-core --><dependency><groupId>net.oschina.j2cache</groupId><artifactId>j2cache-cor…

【数据挖掘】国科大刘莹老师数据挖掘课程作业 —— 第三次作业

Written Part 1. 基于表 1 1 1 回答下列问题&#xff08;min_sup40%, min_conf75%&#xff09;&#xff1a; Transaction IDItems Bought0001{a, d, e}0024{a, b, c, e}0012{a, b, d, e}0031{a, c, d, e}0015{b, c, e}0022{b, d, e}0029{c, d}0040{a, b, c}0033{a, d, e}0038…

[原创]C++98升级到C++20的复习旅途-从汇编及逆向角度去分析“constexpr“关键字

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…

机器视觉最全面试题总结

文章目录 1. 为什么需要做特征归一化、标准化&#xff1f;2. 常用常用的归一化和标准化的方法有哪些&#xff1f;3. 介绍一下空洞卷积的原理和作用4. 怎么判断模型是否过拟合&#xff0c;有哪些防止过拟合的策略&#xff1f;5. 除了SGD和Adam之外&#xff0c;你还知道哪些优化算…

uniapp 使用安卓模拟器运行

uniapp 启动方式有很多种,这里介绍使用模拟器启动uniapp 要使用模拟器启动uniapp肯定少不了安装模拟器(废话) 这里选着浏览器推荐的第一个模拟器mumu模拟器 下载好了mumu安装包后就是安装了,这个过于小白,就不介绍了 2. 查看模拟器的adb端口号, mumu的adb查看端口号与众不同…

6-48.日期类的设计与实现

使用重载运算符&#xff08;&#xff0c;&#xff0c;<<等&#xff09;实现日期类的操作。功能包括&#xff1a; 1&#xff09;设置日期&#xff0c;如果日期设置不符合实际&#xff0c;则设置为默认日期&#xff08;1900年1月1日&#xff09; 2&#xff09;在日期对象中…

Java 表达式引擎

企业的需求往往是多样化且复杂的&#xff0c;对接不同企业时会有不同的定制化的业务模型和流程。我们在业务系统中使用表达式引擎&#xff0c;集中配置管理业务规则&#xff0c;并实现实时决策和计算&#xff0c;可以提高系统的灵活性和响应能力。 引入规则引擎似乎就能解决这个…

数据结构:堆的实现思路

我们之前写过堆的实现代码&#xff1a;数据结构&#xff1a;堆的实现-CSDN博客 这篇文章我们了解一下堆到底是如何实现的 1.堆向下调整算法 现在我们给出一个数组&#xff0c;逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆 向下调…

栈的链式存储(详解)

栈的链式存储 栈的链式存储是通过链表来实现的&#xff0c;每个节点包含一个元素和一个指向下一个节点的指针。链式存储的栈不需要提前分配内存空间&#xff0c;可以动态地增加或减少元素。 在链式存储中&#xff0c;栈顶元素通常是链表的头节点&#xff0c;栈底元素是链表的…

高光谱遥感影像分类项目开源

热烈欢迎大家在git上star&#xff01;&#xff01;&#xff01;冲鸭&#xff01;&#xff01;&#xff01; 我研究生期间主要是做高光谱遥感影像分类的&#xff0c;毕业论文也是基于深度学习的高光谱遥感影像分类课题&#xff0c;转眼间已经毕业四年了&#xff0c;如今把这块材…

每天一点python——day84

#每天一点Python——84 #异常处理机制try—except—else #异常处理机制try—except—else如果try块中没有抛出异常&#xff0c;则执行else块&#xff0c;如果try中抛出异常&#xff0c;则执行except块#示例&#xff1a; try:a int(input(请输入第一个整数&#xff1a;))b in…

SpringBootAdmin监控原理Actuator,自定义指标

SpringBootAdmin监控原理Actuator&#xff0c;自定义指标 文章目录 SpringBootAdmin监控原理Actuator&#xff0c;自定义指标actuator自定义info端点信息自定义Health端点信息自定义metrics端点信息端点的自定义 actuator JMX方式就是在cmd控制台输入jconsole&#xff0c;会弹出…

递归实现组合型枚举

递归实现组合型枚举 #include<iostream> #include<vector>int n, m; std::vector<int>res; bool st[30];void Print() {for(int i0;i<res.size();i){printf("%d ",res[i]);}puts(""); }void dfs(int num) {if (res.size() m){Print(…

使用docker切换任意版本cuda使用GPU

1.现存问题 在主机上运行很多下载来的机器学习代码时&#xff0c;这些大都运行在不同版本的tensorflow-gpu或者pytorch等的包下的。但是&#xff0c;运行代码的过程中&#xff0c;存在匹配的问题&#xff1a; 1.不同的版本的tensorflow-gpu或pytorch对cuda版本的适配也不一样…

Upsert 及冲突(GORM)

GORM支持了数据库的upsert操作 upsert操作对于插入一条数据而言的&#xff0c;如果插入数据之前&#xff0c;没有这条数据&#xff0c;则会插入该条数据&#xff1b;如果插入数据之前就存在这条数据&#xff08;索引值&#xff09;&#xff0c;就更新这条记录。 创建结构体 …

html电子签名

html电子签名 html5实现手写签名板&#xff0c;点击保存即可生成base64格式的图片 使用H5自带的canvas&#xff0c;无需引入js无需引入别的js 效果图 html代码 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"…