【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

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

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

相关文章

K8S基础简介

用于自动部署,扩展和管理容器化应用程序的开源系统。 功能: 服务发现和负载均衡; 存储编排; 自动部署和回滚; 自动二进制打包; 自我修复; 密钥与配置管理; 1. K8S组件 主从方式架…

socket编程常见操作

1、连接的建立 分为两种:服务端处理接收客户端的连接;服务端作为客户端连接第三方服务 //作为服务端 int listenfd socket(AF_INET, SOCK_STREAM, 0); bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) listen(listenfd, 10); //…

SARscape打开Sentinel1A SAR SLC产品(CSDB_20240630)

1.打开envi,在右侧工具包栏输入“sentinel-1”,并点击打开工具包。 2. 弹出文件导入界面,点击右侧Browse按钮。 3. 选在本地下载好的Sentinel1产品,文件路径最好全是英文,不要出现中文和特殊字符。 4 点击下方“Exec”…

BLACKBOX.AI:解锁编程学习新纪元,加速开发的AI得力助手

文章目录 💯BLACKBOX.AI 官网🍁1 BLACKBOX.AI 工具使用教程🍁2 BLACKBOX.AI工具使用界面介绍🍁3 Chat(聊天)功能🍁4 Explore (探索)功能💎4.1 Terminal(终端)功能💎4.2 Discover(发现)功能&…

ros1仿真导航机器人 hector_mapping gmapping

仅为学习记录和一些自己的思考&#xff0c;不具有参考意义。 1 hector_mapping 建图过程 &#xff08;1&#xff09;gazebo仿真 roslaunch why_simulation why_slam.launch <launch><!-- We resume the logic in empty_world.launch, changing only the name of t…

助农扶贫网站

摘要&#xff1a;随着信息科技的快速发展和互联网的普及&#xff0c;信息技术在助力农业发展、促进农村振兴以及扶贫工作中发挥着越来越重要的作用。本文基于Spring Boot框架和Vue.js前端开发技术&#xff0c;设计完成了一个助农扶贫电商网站。网站提供便捷的农产品信息发布、农…

three.js - MeshStandardMaterial(标准网格材质)- 金属贴图、粗糙贴图

金属贴图、粗糙贴图 金属贴图&#xff1a;metalnessMap 和 粗糙贴图&#xff1a;roughnessMap&#xff0c;是用于模拟物体表面属性的两种重要贴图技术&#xff0c;这两种贴图&#xff0c;通常与基于物理的渲染&#xff08;PBR&#xff09;材质&#xff08;如&#xff1a;MeshSt…

DC-DC产品设计PCB注意事项

DC-DC的电路比LDO会复杂很多&#xff0c;噪声也更大&#xff0c;布局和layout要求更高&#xff0c;layout的好坏直接影响DC-DC的性能&#xff0c;所以了解DC-DC的layout至关重要。 一、Bad Layout EMI&#xff0c;DC-DC的SW管脚上面会有较高的dv/dt&#xff0c; 比较高的dv/d…

FastAPI教程II

本文参考FastAPI教程https://fastapi.tiangolo.com/zh/tutorial Cookie参数 定义Cookie参数与定义Query和Path参数一样。 具体步骤如下&#xff1a; 导入Cookie&#xff1a;from fastapi import Cookie声明Cookie参数&#xff0c;声明Cookie参数的方式与声明Query和Path参数…

HIVE每日一题

select * from sku_info order by sku_id ; 为什么结果没有顺序排序。什么原因导致的&#xff1f;

小红书多账号管理平台哪个好用?可以快速监测多个小红书账号的数据吗?

随着品牌营销战线的不断扩展&#xff0c;小红书已经成为企业和个人品牌竞相展示的舞台。但是&#xff0c;随之而来的多账号管理问题也让众多运营者头疼不已。一个优秀的多账号管理平台&#xff0c;能让你事半功倍&#xff0c;轻松监控和分析账号数据。 如今&#xff0c;市面上出…

SiteSucker Pro for Mac:一键下载整站,轻松备份与离线浏览!

SiteSucker Pro for Mac是一款专为苹果电脑用户设计的网站下载与备份工具&#x1f578;️。它以其强大的整站下载能力和用户友好的界面&#xff0c;成为了众多Mac用户备份网站、离线浏览的得力助手&#x1f4bb;。 这款软件允许用户一键下载整个网站&#xff0c;包括所有的网页…

鸿蒙开发Ability Kit(程序框架服务):【选择申请权限的方式】

选择申请权限的方式 应用在访问数据或者执行操作时&#xff0c;需要评估该行为是否需要应用具备相关的权限。如果确认需要目标权限&#xff0c;则需要在应用安装包中申请目标权限。 每一个权限的权限等级、授权方式不同&#xff0c;申请权限的方式也不同&#xff0c;开发者在…

java spring 拦截器和过滤器+过滤器处理数据

java spring 拦截器和过滤器过滤器处理数据 介绍Spring拦截器&#xff08;Interceptor&#xff09;导入依赖完整代码 Java过滤器&#xff08;Filter&#xff09;完整代码分为 2 个文件&#xff0c;请看下面BodyReaderHttpServletRequestWrapper.javaMyFilter.java Spring过滤器…

DataWhale-吃瓜教程学习笔记 (五)

学习视频&#xff1a;第4章-决策树_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 第四章 4.1&#xff1b;4.2 文章目录 决策树算法原理- 逻辑角度- 几何角度 ID3 决策树- 自信息- 信息熵 &#xff08;自信息的期望&#xff09;- 条件熵 &#xff08; Y 的信息熵关于概率分布 …

python selenium 打开网页

selenium工具类 - 文件名 seleniumkit.py 代码如下 # -*- coding:utf-8 _*-from selenium import webdriverimport os import timefrom selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from seleniu…

Ascend基于自定义算子工程的算子开发

环境准备 见https://gitee.com/zaj1414904389/ascend-tutorial.git 工程创建 CANN软件包中提供了工程创建工具msopgen&#xff0c;开发者可以输入算子原型定义文件生成Ascend C算子开发工程 [{"op": "AddCustom","input_desc": [{"name…

CVPR2024 轨迹预测系列(一)

CVPR2024 轨迹预测系列&#xff08;一&#xff09; 文章目录 CVPR2024 轨迹预测系列&#xff08;一&#xff09;一、Adapting to Length Shift: FlexiLength Network for Trajectory Prediction.1、Abstract2、FlexiLength Network3、Datasets4、Experiments5、Implementation …

分类预测 | PSO-PNN基于粒子群算法优化概率神经网络的数据分类预测(Matlab)

分类预测 | ZOA-PCNN-AT-SVM斑马优化并行卷积-支持向量机融合注意力机制的故障识别 目录 分类预测 | ZOA-PCNN-AT-SVM斑马优化并行卷积-支持向量机融合注意力机制的故障识别分类效果基本描述程序设计参考资料 分类效果 基本描述 1.PSO-PNN基于粒子群算法优化概率神经网络的数据…

长鑫存储母公司斥资24亿美元发展国产HBM

国产DRAM厂商长鑫存储母公司睿力集成计划投资24亿美元在上海建一座高端封装工厂。据报道&#xff0c;该工厂将专注于高带宽存储器&#xff08;HBM&#xff09;芯片的封装&#xff0c;预计到2026年中开始投入生产。长鑫存储将利用来自多方投资者的资金进行建设&#xff0c;其中包…