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

news2024/9/9 0:27:36

💬 写在前面:在函数式编程中,除了递归函数外,还经常使用高阶函数。高阶函数是指接收其他函数作为参数或返回另一个函数的函数。高阶函数通过抽象编程模式以实现重用,使程序可以在更高层次上进行编写。让我们重点看看常用的高阶函数,如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.coloradmin.cn/o/1878261.html

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

相关文章

socket编程常见操作

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

JS(JavaScript)二级菜单级联案例演示

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

Bazel plugin for Visual Studio Code

语法突出显示 Bazel Build Targets 树显示工作区中的构建包/目标BUILD 文件中的 CodeLens 链接可通过单击目标直接启动构建或测试Buildifier 集成以检查和格式化您的 Bazel 文件(需要安装 Buildifier)tasks.json 的 Bazel Task 定义在构建期间调试 .bzl…

排序【插入排序】

排序的概念 排序:所谓排序,就是将一份数据,通过某个或者某些关键字的大小,进行递增或者递减排序的操作。 稳定性:假定在待排序的数据组中,存在多个相同的元素,若经过排序,这些数据…

【论文阅读】transformer及其变体

写在前面: transformer模型已经是老生常谈的一个东西,以transformer为基础出现了很多变体和文章,Informer、autoformer、itransformer等等都是顶刊顶会。一提到transformer自然就是注意力机制,变体更是数不胜数,一提到…

【ARM】Ulink不同的系列对于芯片的支持和可以支持keil软件

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 了解不同版本的ULINK可以支持的芯片架构,和ULINK可以和哪个系列的keil软件进行在线调试 2、 问题场景 用于了解不同ULINK仿真器对于芯片的支持是不一样的,并不是ULINK可以支持所有的keil软件…

搭建Renesas R7FA8D1BHECBD-BTB的开发调试环境(DAP-LINK: N32G45XVL-STB)

目录 概述 1 软硬件 1.1 软硬件环境信息 1.2 开发板信息 1.3 调试器信息 2 FSP和KEIL产生测试项目 2.1 FSP生成项目 2.2 Keil中配置 3 硬件连接框图 4 一个测试案例 4.1 功能介绍 4.2 定时器函数 5 测试 搭建Renesas R7FA8D1BHECBD-BTB的开发调试环境&#xff08…

学会python——在Excel中生成图表数据(python实例十五)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.生成表格数据 3.1 代码构思 3.2 代码示例 4.绘制图表 4.1 代码构思 4.2 代码示例 5.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 P…

《概率论与数理统计》期末笔记_下

目录 第4章 随机变量的数字特征 4.1 数学期望 4.2 方差 4.3 常见分布的期望与方差 4.4 协方差与相关系教 第5章 大数定律和中心极限定理 5.1 大数定律 5.2 中心极限定理 第6章 样本与抽样分布 6.1 数理统汁的基本概念 6.2 抽样分布 6.2.1 卡方分布 6.2.2 t分布 6.…

window搭建git环境

1.下载安装window下git专用软件scm 从Git for Windows 官网网站下载,并且一路安装即可 安装成功后通过桌面快捷图标Git Bash点击打开 安装后软件应该会自动帮助配置环境变量,如果没有需要自己配置使用 2.git环境配置 2.1设置姓名和邮箱(github上你注…

006-GeoGebra基础篇-GeoGebra划定区域导出gif动图

上一篇末尾放了一个成果展示,有朋友问客F怎么用GeoGebra导出gif动画,也好插入到笔记里或者直接放到PPT里(插入PPT有专门教程,可以看003),本篇主要来介绍一下gif导出操作 这里写目录标题 一、成品展示二、操…

19.异常处理

学习知识:方法中,异常的抛出和捕获 Main.java: public class Main {public static void main(String[] args) {errtest errtest new errtest();try{errtest.testerr();} catch (ArithmeticException e) {System.out.println("这个方法…

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

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

追觅科技25届校招校招24年社招科技北森题库商业推理综合测评答题攻略、通关技巧

一、追觅科技这家公司怎么样? 追觅科技是一家在智能清洁家电领域表现出色的企业。 二、追觅科技待遇怎么样 追觅科技的待遇在业内具有竞争力,具体信息如下: 1. **薪酬结构**:根据对外经济贸易大学招生就业处发布的2023届校园招…

web安全渗透测试十大常规项(一):web渗透测试之Fastjson反序列化

渗透测试之Java反序列化 1. Fastjson反序列化1.1 FastJson反序列化链知识点1.2 FastJson反序列化链分析1.3.1 FastJson 1.2.24 利用链分析1.3.2 FastJson 1.2.25-1.2.47 CC链分析1.3.2.1、开启autoTypeSupport:1.2.25-1.2.411.3.2.2 fastjson-1.2.42 版本绕过1.3.2.3 fastjson…

VS2019+QT5.12.10: error MSB4036: 未找到“Join”任务。请检查下列各项: 1.) 项目文件中的任务名

1、背景 两个VS2019打开两个相同的项目,一个里可以正常运行, 一个中一直报错,,报的错也是瞎几把报的。。 2、重新安装插件 之前在VS的扩展中在线安装了qt插件, 安装了一半,比较慢,直接强行退出…

论文工具使用---connected papers

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

Python逻辑控制语句 之 判断语句--石头剪刀布案例

需求: 1. 从控制台输入要出的拳 —— 石头(1)/剪刀(2)/布(3) 2. 电脑随机出拳 —— 先假定电脑只会出石头,完成整体代码功能 3. 比较胜负 胜负规则&#x…

项目训练营第六天

项目训练营第六天 前端注册界面开发 1、基本布局 将登录界面文件夹复制一份,粘贴到上一层目录User目录下,改名为Register 在typings.d.ts文件中添加如下的登录前端接口函数标准参数 以及返回值类型 2、接口开发 在api.ts中修改原先接口如下&…

Django 自定义过滤器

1,编写自定义过滤器并注册 创建目录 Test/app5/templatetags 分别创建文件 Test/app5/templatetags/__init__.py Test/app5/templatetags/myfilter.py 添加过滤器脚本 Test/app5/templatetags/myfilter.py from django import template register template.…