【PL理论深化】(12) Ocaml 语言:高阶函数 | map 函数 | filter 函数 | fold 函数

💬 写在前面:在函数式编程中,除了递归函数外,还经常使用高阶函数。高阶函数是指接收其他函数作为参数或返回另一个函数的函数。高阶函数通过抽象编程模式以实现重用,使程序可以在更高层次上进行编写。让我们重点看看常用的高阶函数,如map、filter 和 fold。

目录

0x00 map 函数

0x01 filter 函数

0x02 fold 函数


0x00 map 函数

下面定义的三个函数 inc_all、square_all 和 cube_all

分别是将给定列表的每个元素增加、平方和立方的函数。

let rec inc_all l =
  match l with
  | [] -> []
  | hd::tl -> (hd+1)::(inc_all tl)

let rec square_all l =
  match l with
  | [] -> []
  | hd::tl -> (hd*hd)::(square_all tl)

let rec cube_all l =
  match l with
  | [] -> []
  | hd::tl -> (hd*hd*hd)::(cube_all tl)

这些函数都是按照共同的模式定义的。

唯一的区别在于应用于列表各元素的操作。如果将这种操作一般称为 f

那么上述三个函数所共享的编程模式可以用如下的高阶函数 map 来表示。

let rec map f l =
  match l with
  | [] -> []
  | hd::tl -> (f hd)::(map f tl)

map 函数除了接收列表 l 之外,还接收一个函数 f 作为参数,

f 表示要应用于每个元素的操作。map 函数生成一个新列表,

其中列表 l 的每个元素 a 都被替换为 f a 的值。也就是说,

如果 l 是列表 [a1; a2; ...; ak] ,那么 map f l 将生成一个新列表 [f a1; f a2; ...; f ak] 。

通过查看 map 的类型,我们可以理解它的大致含义:

所应用函数的类型是 'a \rightarrow {}'b,表示接收一个 'a 类型的列表作为输入,

并生成一个 {}'b 类型的列表作为输出。

一般来说,函数的类型可以被视为该函数所执行任务的摘要 (抽象) 。

利用高阶函数 map,可以简洁地定义上述三个函数如下:

let inc_all l = map (fun x -> x + 1) l
let square_all l = map (fun x -> x * x) l
let cub_all l = map (fun x -> x * x * x) l

或者,可以省略共同的参数 l,定义如下:

let inc_all = map (fun x -> x + 1)
let square_all = map (fun x -> x * x)
let cub_all = map (fun x -> x * x * x)

将其与前面的定义进行比较,可以发现,

使用 map 的定义使每个函数的含义在更高层次上显得更加清晰和直接。

例如,square_all 的含义直接表现为对列表 l 的每个元素应用函数 fun x -> x * x

而如果不使用 map 而是递归编写的话,这种含义就隐藏在代码中,不会直接显现。

一个写得好的程序是其他人可以轻松理解的程序,因为即使不知道实现的细节,

也能在高层次上理解它。从这个角度来看,一种好的编程语言应当能够在更高层次上表达想法。

在函数式编程中,高阶函数在提升程序的抽象层次方面发挥了重要作用。

0x01 filter 函数

以下两个函数虽然执行的任务不同,但它们是按照相同的模式定义的:

let rec even l =
  match l with
  | [] -> []
  | hd::tl ->
    if hd mod 2 = 0 then hd::(even tl)
    else even tl
let rec greater_than_five l =
  match l with
  | [] -> []
  | hd::tl ->
    if hd > 5 then hd::(greater_than_five tl)
    else greater_than_five tl

even 函数从列表l中选择偶数,而 greater_than_five 函数选择大于 5 的数。

换句话说,它们都是从给定列表中选择满足特定条件的元素的函数,只是选择条件不同。

可以通过以下高阶函数来抽象化这种共同模式:

let rec filter p l =
  match l with
  | [] -> []
  | hd::tl ->
    if p hd then hd::(filter p tl)
    else filter p tl

filter 函数接收一个函数 p 和一个列表 l 作为参数,

它的工作是收集列表l中满足函数 p 条件的元素。

这里函数 p 应该是一个返回布尔值的函数 (谓词) 。换言之,filter 的类型应该如下所示:

可以利用高阶函数 filter 来定义上述两个函数:

let even l = filter (fun x -> x mod 2 = 0) l
let greater_than_five l = filter (fun x -> x > 5) l

0x02 fold 函数

在函数式编程中,最常用的高阶函数之一是 fold,让我们来比较一下下面这两个函数:

let rec sum l =
  match l with
  | [] -> 0
  | hd::tl -> hd + (sum tl)

let rec prod l =
  match l with
  | [] -> 1
  | hd::tl -> hd * (prod tl)

这两个函数都遵循对给定列表的每个元素进行迭代并累积应用某种操作的模式。

例如,对于列表  [1; 2; 3] ,应用上述两个函数的过程如下所示:

这两个函数的区别在于它们应用的累积操作和初始值。

例如,对于 sum 函数,操作符是 +,初始值是 0;

而对于 prod 函数,操作符是 *,初始值是 1。

我们可以定义一个高阶函数 fold_right,它接受这两个额外的参数,如下所示:

let rec fold_right f l a =
  match l with
  | [] -> a
  | hd::tl -> f hd (fold_right f tl a)

利用 fold_right 可以如下定义函数 sum 和 prod,其中 f 是累积操作,l 是列表,a 是初始值。

let sum lst = fold_right (fun x y -> x + y) lst 0
let prod lst = fold_right (fun x y -> x * y) lst 1

每个函数的含义更直接地在更高级别上表达出来。sum 函数从初始值 0 开始,

对列表中的每个元素进行累加运算;而 prod 函数从初始值 1 开始,

对列表中的每个元素进行累乘运算。

考虑到 sum prod 如何以尾递归函数的形式编写,其含义与上述示例相同:

let rec sum a l =
  match l with
  | [] -> a
  | hd::tl -> sum (a + hd) tl
let rec prod a l =
  match l with
  | [] -> a
  | hd::tl -> prod (a * hd) tl

将定义了上述两个函数的共同模式的高阶函数称为 fold_left

let rec fold_left f a l =
  match l with
  | [] -> a
  | hd::tl -> fold_left f (f a hd) tl

使用 fold_left 定义 sum prod 的方式如下:

let sum a l = fold_left (fun x y -> x + y) a l
let prod a l = fold_left (fun x y -> x * y) a l

让我们更详细地比较一下 fold_rightfold_left

首先,它们的类型有所不同,如下所示:

更重要的区别在于累积操作的方向不同。

fold_right 从列表的最后一个元素开始,向左依次累积。

相反,fold_left 从列表的最左边元素开始,向右依次累积:

因此,如果应用的操作 f 不满足结合律,两个结果可能会不同:

# fold_right (fun x y -> x - y) [1;2;3] 0;;
- : int 2
# fold_left (fun x y -> x - y) 0 [1;2;3];;
- : int = -6

最后,fold_left 是尾递归函数。在列表长度较长的情况下,最好尽量使用 fold_left

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2024.6.20
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

- R. Neapolitan, Foundations of Algorithms (5th ed.), Jones & Bartlett, 2015.

- T. Cormen《算法导论》(第三版),麻省理工学院出版社,2009年。

- T. Roughgarden, Algorithms Illuminated, Part 1~3, Soundlikeyourself Publishing, 2018.

- J. Kleinberg&E. Tardos, Algorithm Design, Addison Wesley, 2005.

- R. Sedgewick&K. Wayne,《算法》(第四版),Addison-Wesley,2011

- S. Dasgupta,《算法》,McGraw-Hill教育出版社,2006。

- S. Baase&A. Van Gelder, Computer Algorithms: 设计与分析简介》,Addison Wesley,2000。

- E. Horowitz,《C语言中的数据结构基础》,计算机科学出版社,1993

- S. Skiena, The Algorithm Design Manual (2nd ed.), Springer, 2008.

- A. Aho, J. Hopcroft, and J. Ullman, Design and Analysis of Algorithms, Addison-Wesley, 1974.

- M. Weiss, Data Structure and Algorithm Analysis in C (2nd ed.), Pearson, 1997.

- A. Levitin, Introduction to the Design and Analysis of Algorithms, Addison Wesley, 2003. - A. Aho, J. Hopcroft, and J. Ullman, Data Structures and Algorithms, Addison-Wesley, 1983.

- E. Horowitz, S. Sahni and S. Rajasekaran, Computer Algorithms/C++, Computer Science Press, 1997.

- R. Sedgewick, Algorithms in C: 第1-4部分(第三版),Addison-Wesley,1998

- R. Sedgewick,《C语言中的算法》。第5部分(第3版),Addison-Wesley,2002

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Webpack: 核心配置结构

概述 Webpack 是一种 「配置」 驱动的构建工具,所以站在应用的角度,必须深入学习 Webpack 的各项配置规则,才能灵活应对各种构建需求。本文将作为小册应用系列的一个总结,汇总与应用配置相关的各项知识点,包括&#x…

酒店客房管理系统(Java+MySQL)

技术栈 Java: 作为主要编程语言。Swing GUI: 用于开发图形用户界面。MySQL: 作为数据库管理系统。JDBC: 用于连接和操作MySQL数据库。 功能要点 管理登录认证 系统提供管理员登录认证功能。通过用户名和密码验证身份,确保只有授权的用户可以访问和管理酒店客房信…

数据结构复习指南

数据结构复习指南 本文中列举了数据结构期末考试可能存在的考点 绪论 数据的基本单位 数据元素是数据的基本单位 数据项 数据项是组成数据的、有独立含义的、不可分割的最小单位。 数据对象 数据对象是性质相同的数据元素的集合,是数据的一个子集。 数据结…

KBL410-ASEMI智能AI专用整流桥KBL410

编辑:ll KBL410-ASEMI智能AI专用整流桥KBL410 型号:KBL410 品牌:ASEMI 封装:KBL-4 正向电流(Id):4A 反向耐压(VRRM):1000V 正向浪涌电流:2…

系统运维面试总结(系统权限)

系统运维面试总结(系统权限) 一、权限优化简述Linux权限划分原则二、备份策略三、Raid四、资源查看五、Linux启动流程 一、权限优化简述Linux权限划分原则 ckhunter也是一款常用的Linux杀毒软件 不可修改但可删除 二、备份策略 供参考较为全面的备份方案…

【操作系统】进程管理——进程的概念、组成和特征(个人笔记)

学习日期:2024.6.29 内容摘要:进程的基本概念和特征、状态和转换 进程的概念 程序与进程 程序:是静态的,是存放在磁盘里的可执行文件,就是一系列的指令集合 进程(Process):是动态…

基于STM32的智能家用电力管理系统

目录 引言环境准备智能家用电力管理系统基础代码实现:实现智能家用电力管理系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景:电力管理与优化问题解决方案与优化收尾与总结 1. 引言 智能家用电力管理系统通…

6.优化算法之模拟

1.替换所有的问号 . - 力扣&#xff08;LeetCode&#xff09; class Solution {public String modifyString(String s) {char[] sss.toCharArray();int nss.length;for(int i0;i<n;i){if(ss[i]?){for(char cha;ch<z;ch){if((i0||ss[i-1]!ch)&&(in-1||ss[i1]!c…

湖南(市场调研)源点咨询 市场研究中定性调研的优势与局限性

定性调研指的是调研的结果不经量化或数量分析。 它通常用于分析态度、感觉和动机。定性调研特别是焦点小组访谈法还在继续普及&#xff0c;原因有以下三个&#xff1a; 第一&#xff0c;定性调研通常比定量调研成本低&#xff1b; 第二&#xff0c;定性调研在了解消费者内心…

滑动窗口2

1. 水果成篮&#xff08;904&#xff09; 题目描述&#xff1a; 算法原理&#xff1a; 根据题目意思&#xff0c;friuts表示第i棵树上的水果种类&#xff0c;然后我们有两个篮子去在这些树上去采水果&#xff0c;但是有限制就是一个篮子里就只能装一种水果&#xff0c;也就是…

【简单讲解下OneFlow深度学习框架】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

SM2258XT量产工具,SM2258XT开卡三星SSV4颗粒成功分享,SM2259XT量产参考教程,威刚ADATA SP580开卡记录

前两天拆了笔记本上的威刚ADATA SP580 240GB&#xff0c;准备做移动硬盘用&#xff0c;装入移动硬盘盒之后接入电脑&#xff0c;发现系统可认盘&#xff0c;SMART显示正常&#xff0c;Windows的磁盘管理能显示正确容量&#xff0c;但处于未初始化状态&#xff0c;且始终无法初始…

病理性不对称引导的渐进学习用于急性缺血性脑卒中梗死分割| 文献速递-先进深度学习疾病诊断

Title 题目 Pathological Asymmetry-Guided Progressive Learning for Acute Ischemic Stroke Infarct Segmentation 病理性不对称引导的渐进学习用于急性缺血性脑卒中梗死分割 01 文献速递介绍 中风已经成为第二大致命疾病&#xff0c;大约70%的中风是缺血性的。众所周知…

AI in Law 法律领域AI应用-基于DeepNLP AI App Store 评论评测和排名

来源: quora 社区: https://deepnlpaistore.quora.com/ github: https://github.com/rockingdingo/deepnlp/blob/master/store/law.md 法律领域哪些AI服务应用更能满足用户的需求&#xff0c;排名最高? 参考deepnlp.org网站根据用户真实评论打分和show case分享&#xff0c;分…

java基于ssm+jsp 二手手机回收平台系统

1前台首页功能模块 二手手机回收平台系统&#xff0c;在系统首页可以查看首页、手机商城、新闻资讯、我的、跳转到后台、购物车等内容&#xff0c;如图1所示。 图1前台首页功能界面图 用户注册&#xff0c;在用户注册页面可以填写账号、密码、姓名、手机、邮箱、照片、地址、…

论文工具使用---connected papers

如何使用connected papers 使用方法具体功能其他资源 官网地址&#xff1a;connected papers &#xff1a;一个旨在帮助科研工作者快速搜索文献的全新工具&#xff0c;可以清晰的查看文献的引文信息&#xff0c;了解文献的引用和被引用关联。 使用方法 输入论文标题后&#xf…

如何配置Redis + Rdis在IDEA中的使用

文章目录 Step1. 下载zipStep2. 修改环境变量Step3. 启动Redis服务端Step4. 启动Redis客户端Step5. IDEA中链接Redis Step1. 下载zip 下载 Redis-x64-xxx.zip压缩包&#xff0c;解压到 E 盘后&#xff0c;将文件夹重新命名为 redis 下载地址&#xff1a;Redis下载地址 Step2…

Java----面向对象----总复习

面向对象 面向对象的程序设计思想(Object Oriented Programming),简称OOP.是一种设计者思想.关注的焦点是类,参照现实中的事务,将事务的属性特征,行为抽象出来,用类来表示.代码结构:以类为组织单位,每种事务都有自己的属性和行为,功能, 思想:从宏观上 帮助我们把握,整体分析整…

C语言的数据结构:树与二叉树(哈夫曼树篇)

前言 上篇讲完了二叉树&#xff0c;二叉树的查找性能要比树好很多&#xff0c;如平衡二叉树保证左右两边节点层级相差不会大于1&#xff0c;其查找的时间复杂度仅为 l o g 2 n log_2n log2​n&#xff0c;在两边层级相同时&#xff0c;其查找速度接近于二分查找。1w条数据&am…

160相交链表

解法1&#xff1a; public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {// 定义两个指针。// 获得两个链表的长度&#xff0c;将较长的链表先用指针移动到和短链表一样的长度。// 再一个个比较ListNode l1 headA, l2 headB;int …