聊聊函数式编程中的“式”
当谈到函数式编程的“式”时,通常指的是函数的组合、转换和应用,以及处理数据的方式和风格。在函数式编程中,式是用来构建程序逻辑的基本单元。
下面更详细解释函数式编程中的几个关键式:
函数的组合:
函数式编程中,将多个函数组合成一个新的函数是常见的操作。函数组合可以通过函数的返回值作为另一个函数的输入来实现,实现函数的复用和组合。
示例:假设有两个函数 f 和 g ,函数 g 的输入为函数 f 的输出,可以通过函数的组合来实现: h = g(f(x))。
const compose = f => g => x => f(g(x));
const f = compose (x => x * 4) (x => x + 3);
f(2) // 20
上面代码中,compose就是一个函数合成器,用于将两个函数合成一个函数。
函数的转换
函数式编程中,可以对函数进行一系列转换,例如柯里化(Currying)、部分应用 (Partial Application)等。这些转换可以将函数的参数进行重组或固定,不仅使得函数更灵活,还能简化函数调用的方式。
- 柯里化指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只接受一个参数(unary)。
function add (a, b) {return a + b;
}
add(1, 1) // 2
上面代码中,函数add接受两个参数a和b。
柯里化就是将上面的函数拆分成两个函数,每个函数都只接受一个参数。
function add (a) {return function (b) {return a + b;}
}
// 或者采用箭头函数写法
const add = x => y => x + y;
const f = add(1);
f(1) // 2
- 部分应用(Partial Application)是一种函数式编程的技术,它允许我们固定函数的一部分参数,并返回一个新的函数。这样做可以减少函数调用时需要提供的参数数量,从而使函数更加灵活和可重用。
举一个简单业务实例理解一下:
假设在我们的业务中,我们经常要给同一个用户发送邮件,主题和内容都是固定的,只需要提供收件人地址就可以了。我们可以使用部分应用来创建一个新的函数,该函数只需要提供收件人地址就可以调用 sendEmail,而无需每次都重复输入主题和内容。
function sendEmail(to, subject, message) {// 发送电子邮件的逻辑代码console.log(`发送电子邮件给 ${to},主题为 ${subject},内容为 ${message}`);
}const sendWelcomeEmail = sendEmail.bind(null, "hello@example.com", "欢迎加入我们");sendWelcomeEmail("user1@example.com"); // 调用新函数,输出:发送电子邮件给 user1@example.com,主题为 欢迎加入我们,内容为 undefined
sendWelcomeEmail("user2@example.com"); // 调用新函数,输出:发送电子邮件给 user2@example.com,主题为 欢迎加入我们,内容为 undefined
在上述示例中,我们使用 bind() 方法将 sendEmail 函数的前两个参数(固定的主题和内容)绑定为 "hello@example.com" 和 "欢迎加入我们"。然后,我们创建一个新的函数 sendWelcomeEmail,该函数只需要提供收件人地址作为参数。
每当我们调用 sendWelcomeEmail 函数时,它会自动将绑定的参数 "hello@example.com"、"欢迎加入我们",以及传递的收件人地址一起传递给 sendEmail 函数来发送欢迎邮件。
函数的应用
函数式编程中,函数的应用是指将函数应用于输入数据,通过函数对数据进行转换、过滤、聚合等操作。函数的应用通常采用高阶函数的方式,即将函数作为参数传递给另一个函数。
示例:在函数式编程中,常用 map、reduce、filter 等高阶函数来对列表或集合中的元素进行转换、合并或筛选。
数据的处理方式和风格
函数式强调实用纯函数和不可变性来处理数据。
函数式编程中,常常使用不可变数据结构来表示数据,并通过创建新的数据结构来进行操作和更新,而不是直接修改原来的数据。
- 纯函数纯函数是指对于相同的输入,总是返回相同的输出,且没有任何副作用。
- 加法函数:
function add(a, b) {return a + b;
}
这个函数是纯函数,因为它只是接收两个参数并返回它们的和。它没有副作用,不会修改任何外部状态,也不依赖于可变的数据。
- 平方函数:
function square(x) {return x * x;
}
这个函数也是纯函数。对于相同的输入值,它总是返回相同的输出值。它没有副作用,不会改变任何外部环境,也不依赖于外部状态。
- 数组排序函数:
function sortArray(arr) {return arr.sort();
}
这个函数不是纯函数,因为它直接修改了传入的数组,并且返回修改后的数组。它有副作用,修改了传入的参数,可能会影响到其他代码对该数组的引用。
- 获取当前时间函数:
function getCurrentTime() {return new Date().getTime();
}
这个函数也不是纯函数,因为它会依赖外部状态(当前时间),每次调用都会返回不同的输出。它对外部的时间状态有依赖,因此在不同的时间点调用会返回不同的结果。
- 不可变性是指数据一旦创建就无法被修改。在函数式编程中,强调使用不可变数据结构,这样可以避免副作用和意外的修改,从而使代码更可靠、可维护,并且具有更好的并发性。
- 字符串不可变性:
const str = "Hello";
const newStr = str.toUpperCase();console.log(str); // 输出:"Hello"
console.log(newStr); // 输出:"HELLO"
在这个示例中,toUpperCase 方法返回一个新的字符串,它将原始字符串的内容转换为大写。原始字符串 str 仍然保持不变,它始终是 “Hello”。这是因为字符串是不可变的,一旦创建就不能被修改。
- 数组不可变性:
const arr = [1, 2, 3];
const newArr = arr.map(num => num * 2);console.log(arr); // 输出:[1, 2, 3]
console.log(newArr); // 输出:[2, 4, 6]
在这个示例中,map 方法返回一个新的数组,其中每个元素都是原始数组中的元素乘以 2。原始数组 arr 保持不变,它仍然是 [1, 2, 3]。同样,这是因为数组是不可变的数据结构。
- 对象不可变性:
const person = { name: "Alice", age: 30 };
const newPerson = { ...person, age: 31 };console.log(person); // 输出:{ name: "Alice", age: 30 }
console.log(newPerson); // 输出:{ name: "Alice", age: 31 }
在这个示例中,使用展开运算符 ... 创建了一个浅拷贝的新对象 newPerson,其中修改了 age 属性的值。原始对象 person 仍然保持不变,它的值仍然是 { name: "Alice", age: 30 }。这是因为对象也是不可变的数据结构,一旦创建就不能被修改。
相关文章:
聊聊函数式编程中的“式”
当谈到函数式编程的“式”时,通常指的是函数的组合、转换和应用,以及处理数据的方式和风格。在函数式编程中,式是用来构建程序逻辑的基本单元。 下面更详细解释函数式编程中的几个关键式: 函数的组合: 函数式编程中…...
ubuntu目录分析
在Ubuntu根目录下,以下是一些常见文件夹的含义: /bin:存放可执行文件,包含一些基本的命令和工具。 /boot:存放启动时所需的文件,如内核和引导加载程序。 /dev:包含设备文件,用于与硬…...
Python 进阶(三):正则表达式(re 模块)
❤️ 博客主页:水滴技术 🌸 订阅专栏:Python 入门核心技术 🚀 支持水滴:点赞👍 收藏⭐ 留言💬 文章目录 1. 导入re模块2. re模块中的常用函数2.1 re.search()2.2 re.findall()2.3 re.sub()2.4…...
Vue2 第六节 key的作用与原理
(1)虚拟DOM (2)v-for中的key的作用 一.虚拟DOM 1.虚拟DOM就是内存中的数据 2.原生的JS没有虚拟DOM: 如果新的数据和原来的数据有重复数据,不会在原来的基础上新加数据,而是重新生成一份 3. Vue会有虚拟…...
React之组件的生命周期
React之组件的生命周期 一、概述二、整体说明三、挂载阶段四、更新阶段五、卸载阶段 一、概述 生命周期:一个事务从创建到最后消亡经历的整个过程组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程意义:理解组件的生…...
linux -网络编程-多线程并发服务器
目录 1.三次握手和四次挥手 2 滑动窗口 3 函数封装思想 4 高并发服务器 学习目标: 掌握三次握手建立连接过程掌握四次握手关闭连接的过程掌握滑动窗口的概念掌握错误处理函数封装实现多进程并发服务器实现多线程并发服务器 1.三次握手和四次挥手 思考: 为什么…...
Golang之路---02 基础语法——字典
字典 字典(Map 类型),是由若干个 key:value 这样的键值对映射组合在一起的数据结构。 key 不能是切片,不能是字典,不能是函数。 字典初始化 方式:map[KEY_TYPE]VALUE_TYPE //1.var map1 map[string]int…...
Pytorch(三)
一、经典网络架构图像分类模型 数据预处理部分: 数据增强数据预处理DataLoader模块直接读取batch数据 网络模块设置: 加载预训练模型,torchvision中有很多经典网络架构,可以直接调用注意别人训练好的任务跟咱们的并不完全一样,需要把最后…...
Linux——进程控制
目录 1. 进程创建 1.1 fork函数 1.2 fork系统调用内部宏观流程 1.3 fork后子进程执行位置分析 1.4 fork后共享代码分析 1.5 fork返回值 1.6 写时拷贝 1.7 fork常规用法 1.8 fork调用失败的原因 2.进程终止 2.1 进程退出场景 2.2 strerror函数—返回描述错误号的字符…...
剑指 Offer 59 - I. 滑动窗口的最大值 / LeetCode 239. 滑动窗口最大值(优先队列 / 单调队列)
题目: 链接:剑指 Offer 59 - I. 滑动窗口的最大值;LeetCode 239. 滑动窗口最大值 难度:困难 下一篇:剑指 Offer 59 - II. 队列的最大值(单调队列) 给你一个整数数组 nums,有一个大…...
【Linux后端服务器开发】IP协议
目录 一、IP协议概述 二、协议头格式 三、网段划分 四、IP地址的数量限制 五、路由 六、分片和组装 一、IP协议概述 主机:配有IP地址,但是不进行路由控制的设备 路由器:即配有IP地址,又能进行路由控制 节点:主…...
React组件进阶之children属性,props校验与默认值以及静态属性static
React组件进阶之children属性,props校验与默认值以及静态属性static 一、children属性二、props校验2.1 props说明2.2 prop-types的安装2.3 props校验规则2.4 props默认值 三、静态属性static 一、children属性 children 属性:表示该组件的子节点,只要组…...
ceph集群中RBD的性能测试、性能调优
文章目录 rados benchrbd bench-write测试工具Fio测试ceph rbd块设备的iops性能测试ceph rbd块设备的带宽测试ceph rbd块设备的延迟 性能调优 rados bench 参考:https://blog.csdn.net/Micha_Lu/article/details/126490260 rados bench为ceph自带的基准测试工具&am…...
texshop mac中文版-TeXShop for Mac(Latex编辑预览工具)
texshop for mac是一款可以在苹果电脑MAC OS平台上使用的非常不错的Mac应用软件,texshop for mac是一个非常有用的工具,广泛使用在数学,计算机科学,物理学,经济学等领域的合作,这些程序的标准tetex分布特产…...
简单认识redis高可用实现方法
文章目录 一、redis群集三种模式二、 Redis 主从复制1、简介2、作用:3、流程:4.配置主从复制 三、Redis 哨兵模式1、简介2、原理:3、作用:4、哨兵结构由两部分组成,哨兵节点和数据节点:5、故障转移机制:6、…...
搭建git服务器
1.创建linux账户,创建文件 adduser git passwd gitpsw su git pwd cd ~/ mkdir .ssh cd ~/.ssh touch authorized_keys 2.特别重要(单独起一行),给文件设权限 chmod 700 /home/git/.ssh chmod 600 /home/git/.ssh/authorized_keys 3.本地生产密钥并把…...
线程中断机制
如何中断一个线程? 首先一个线程不应该由其他线程来强制中断或者停止,而是应该由线程自己自行停止。所以我们看到线程的stop()、resume()、suspend()等方法已经被标记为过时了。 其次在java中没有办法立即停止一个线程,然而停止线程显得尤为重…...
CollectionUtils工具类的使用
来自:小小程序员。 本文仅作记录 org.apache.commons.collections包下的CollectionUtils工具类,下面说说它的用法: 一、集合判空 通过CollectionUtils工具类的isEmpty方法可以轻松判断集合是否为空,isNotEmpty方法判断集合不为…...
基于Nonconvex规划的配电网重构研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
yolo系列笔记(v4-v5)
YOLOv4 YOLOv4网络详解_哔哩哔哩_bilibili 网络结构,在Yolov3的Darknet的基础上增加了CSP结构。 CSP的优点: 加强CNN的学习能力 去除计算瓶颈。 减少显存的消耗。 结构为: 、 其实还是类似与残差网络的结构,保留下采样之前…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
