【PL理论深化】(8) Ocaml 语言:元组和列表 | 访问元组中的元素 | 列表中的 head 和 tail | 基本列表操作符

news2024/11/26 12:23:09

  • 💬 写在前面:本章我们将探讨 OCaml  中的元组(tuple)和列表(list),它们是函数式编程语言中最常用的数据结构。
  • 目录

    0x00 元组(Tuple)

    0x01 访问元组中的元素

    0x02 列表(list)

    0x03 列表的 head 和 tail

    0x04 基本列表操作符


0x00 元组(Tuple)

元组 (tuple) 是值的 有序集合 (in-order set) ,例如:

元组 (1, "one") 包含了一个整数和一个字符串,其类型可以表示为 int * string

元组 (2, "two", true) 包含了一个整数, 一个字符串和一个布尔值,其类型为 int * string * bool

# let x = (1, "one");;
val x : int * string = (1, "one")
# let y = (2, "two", true);;
val y : int * string * bool = (2, "two", true)

0x01 访问元组中的元素

要访问元组的每个元素,可以使用模式匹配。

例如,可以定义如下函数 fstsnd,用于获取由两个元素组成的元组的第一个和第二个元素。

# let fst p = match p with (x,_) -> x;;
val fst : ’a * ’b -> ’a = <fun>
# let snd p = match p with (_,x) -> x;;
val snd : ’a * ’b -> ’b = <fun>

或者可以直接在函数的参数中使用元组模式,如下所示:

# let fst (x,_) = x;;
val fst : ’a * ’b -> ’a = <fun>
# let snd (_,x) = x;;
val snd : ’a * ’b -> ’b = <fun>

类型 'a * 'b -> 'a 表示函数 fst 接受一个由任意类型 {}'a 和 {}'b 组成的元组作为输入,

并返回类型为 {}'a 的值。通过函数的类型,我们可以推断出函数的大致作用。

不仅在函数参数中,还可以在 let 表达式中使用元组模式。例如,看下面的代码:

# let p = (1, true);;
val p : int * bool = (1, true)
# let (x,y) = p;;
val x : int = 1
val y : bool = true

p 代表元组 (1, true),并将其分解为 x 和 y

0x02 列表(list)

列表 (List) 是具有相同类型的元素序列。

例如, 由数字 1, 2, 3 组成的列表表示为 [1,2,3] ,其类型是 int list

# [1; 2; 3];;
- : int list = [1; 2; 3]

在 OCaml 中,列表中的每个元素用分号 ; 分隔。将每个元素用逗号 , 

分隔的列表 [1, 2, 3] 被识别为包含元组 (1,2,3) 作为其元素的列表 [(1,2,3)] ,

因此需要注意这一点:

# [1,2,3];;
- : (int * int * int) list = [(1, 2, 3)]
# [(1,2,3)];;
- : (int * int * int) list = [(1, 2, 3)]

空列表在 OCaml 中用 [\, ]  表示,其类型是 'a list,表示多态类型:

# [];;
- : ’a list = []

在 OCaml 中,列表的所有元素必须是相同的类型。

例如,[1;true] 在 OCaml 中不是一个列表:

# [1; true];;
Error: This expression has type bool but an expression
was expected of type int

这种限制也源于静态类型系统。

例如,在具有动态类型系统 (如 Python) 的语言中,列表可以包含不同类型的值。

另外,列表是有序元素的序列。因此,下面这两个列表是不同的列表。

# [1;2;3] = [2;3;1];;
- : bool = false

0x03 列表的 head 和 tail

列表的第一个元素称为 head,除第一个元素外剩余的列表称为 tail,即头和尾。

举个例子,对于列表:

 [1; 2;3;4;5;6;7;8] \in \left \langle list \right \rangle

头是 1,尾是 2,3,4,5,6,7,8

一般来说,对于类型为 t 的列表,头的类型是 t,尾的类型是 t list 

列表的元素可以是任意类型的值,当然,每个元素的类型必须相同。

# [1;2;3;4;5];;
- : int list = [1; 2; 3; 4; 5]
# ["OCaml"; "Java"; "C"];;
- : string list = ["OCaml"; "Java"; "C"]
# [(1,"one"); (2,"two"); (3,"three")];;
- : (int * string) list =
    [(1, "one"); (2, "two"); (3, "three")]
# [[1;2;3];[2;3;4];[4;5;6]];;
- : int list list = [[1; 2; 3]; [2; 3; 4]; [4; 5; 6]]

最后一个例子是整数列表的列表 (int list list) 。

在这种情况下, 每个元素对应的列表可以有不同的长度。

# [[1;2;3]; [4]; []];;
- : int list list = [[1; 2; 3]; [4]; []]

列表 [1; 2; 3] 和 [4],尽管长度不同,都是整数列表;

空列表 [\, ] 是多态类型,因此也可以是整数列表。

0x04 基本列表操作符

首先是 ::(读作 cons),它可以在列表的最前面添加一个元素,从而创建一个新的列表。

# 1::[2;3];;
- : int list = [1; 2; 3]
# 1::2::3::[];;
- : int list = [1; 2; 3]

将两个列表连接在一起时,使用 @ (读作 append)。

# [1; 2] @ [3; 4; 5];;
- : int list = [1; 2; 3; 4; 5]

在编写处理列表的函数时,模式匹配经常被使用。

例如,我们可以定义函数 hd 和 tl 来获取列表的头部和尾部。

# let hd l =
    match l with
    | [] -> raise (Failure "hd is undefined")
    | a::b -> a;;
val hd : ’a list -> ’a = <fun>
# let tl l =
    match l with
    | [] -> raise (Failure "tl is undefined")
    | a::b -> b;;
val tl : ’a list -> ’a list = <fun>
# hd [1;2;3];;
- : int = 1
# tl [1;2;3];;
- : int list = [2; 3]

列表的头部和尾部对于空列表是未定义的。

如果列表 l 不是空列表,则可以将其分解为头部 a 和尾部 b

其中 hd 返回 atl 返回 b(如果列表 l 是单元素列表,则尾部 tl 是空列表)。

请注意,在上述定义中,这两个函数的返回类型是不同的。

如果省略异常处理,也可以简单地定义如下:

# let hd (a::b) = a;;
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
[]
val hd : ’a list -> ’a = <fun>
# let tl (a::b) = b;;
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
[]
val tl : ’a list -> ’a list = <fun>

在这种情况下,OCaml 提醒我们函数 hd 和 tl 没有考虑空列表的情况。

虽然这些警告信息不是编译错误,但可能在运行时引发未处理的异常,

建议事先处理所有可能的情况。举个例子,我们可以尝试编写一个函数来计算列表的长度:

# let rec length l =
    match l with
    [] -> 0
    |h::t -> 1 + length t;;
val length : ’a list -> int = <fun>
# length [1;2;3];;
- : int = 3

给定的列表 l 如果是空列表,则定义其长度为 0。

如果不为空,则可以将列表 l 分为头部 h 和尾部 t

此时列表 l 的长度应该等于尾部 t 的长度加 1。

根据上述定义,由于头部 h 没有被使用,可以用下划线 (_) 来表示省略:

let rec length l =
  match l with
    [] -> 0
  |_::t -> 1 + length t

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

📜 参考资料 

- 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/1875274.html

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

相关文章

LabVIEW项目外协时选择公司与个人兼职的比较

​在选择LabVIEW项目外协合作伙伴时&#xff0c;外协公司和个人兼职各有优劣。个人兼职成本较低且灵活&#xff0c;但在可靠性、技术覆盖面、资源和风险管理上存在不足。而外协公司拥有专业团队、丰富资源、完善的项目管理和风险控制&#xff0c;尽管成本较高&#xff0c;但能提…

上海六十中学多功能气膜馆项目:轻空间全速推进

项目进展捷报频传 上海六十中学多功能气膜馆项目土建工作已基本完工&#xff0c;今天轻空间团队正式进场&#xff0c;展开气膜部分的施工。我们将为上海六十中学打造一个现代化、环保、高效的多功能气膜馆&#xff0c;提供优质的运动和活动场所。 现场施工一片繁忙 在施工现场&…

C++ | Leetcode C++题解之第204题计数质数

题目&#xff1a; 题解&#xff1a; class Solution { public:int countPrimes(int n) {vector<int> primes;vector<int> isPrime(n, 1);for (int i 2; i < n; i) {if (isPrime[i]) {primes.push_back(i);}for (int j 0; j < primes.size() && i …

【方案+源码】srm供应商招投标管理系统建设方案及源码实现

SRM供应商管理系统功能建设涵盖&#xff1a; 供应商管理&#xff1a;整合供应商信息&#xff0c;实现全生命周期管理。 采购需求管理&#xff1a;精准把握采购需求&#xff0c;优化采购计划。 采购寻源管理&#xff1a;智能寻源&#xff0c;匹配最佳供应商。 采购合同管理&…

【入门】5分钟了解卷积神经网络CNN是什么

本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/ 目录 一、卷积神经网络的结构1.1.卷积与池化的作用2.2.全连接层的作用 二、卷积神经网络的运算2.1.卷积层的运算2.2.池化的运算2.3.全连接层运算 三、pytorch实现一个CNN例子3.1.模型的搭建3.2.CNN完整训练代码 CNN神…

Golang | Leetcode Golang题解之第203题移除链表元素

题目&#xff1a; 题解&#xff1a; func removeElements(head *ListNode, val int) *ListNode {dummyHead : &ListNode{Next: head}for tmp : dummyHead; tmp.Next ! nil; {if tmp.Next.Val val {tmp.Next tmp.Next.Next} else {tmp tmp.Next}}return dummyHead.Next …

有人物联的串口服务器USR-TCP232-410S基本测试通信和使用方案(485串口和232串口)

1.将 410S(USR-TCP232-410S&#xff0c;简称 410S 下同)的串口通过串口线(或USB 转串口线)与计算机相连接&#xff0c;通过网线将 410S 的网口 PC 的网口相连接&#xff0c;检测硬件连接无错误后&#xff0c;接入我们配送的电源适配器&#xff0c;给 410S 供电。观察指示灯状态…

MCU 是什么?一文了解MCU 产业

MCU&#xff08;Microcontroller Unit&#xff09;&#xff0c;中文名为“微控制器单元”、“单片微型计算机”。MCU 将中央处理器&#xff08;CPU&#xff09;、内存&#xff08;RAM&#xff09;、输入 / 输出界面&#xff08;I/O&#xff09;等等一大堆东西&#xff0c;全部整…

Qt:4.信号和槽

目录 1.信号源、信号和槽&#xff1a; 2.Qt类的继承关系&#xff1a; 3.自定义槽函数&#xff1a; 4.第一种信号和槽的连接的方法&#xff1a; 5.第二种信号和槽的连接的方法&#xff1a; 6.自定义信号&#xff1a; 7.发射信号&#xff1a; 8.信号和槽的传参&#xff1a;…

《数据仓库与数据挖掘》 总复习

试卷组成 第一章图 第二章图 第三章图 第四章图 第五章图 第六章图 第九章图 第一章 DW与DM概述 &#xff08;特点、特性&#xff09; DB到DW 主要特征 &#xff08;1&#xff09;数据太多&#xff0c;信息贫乏&#xff08;Data Rich&#xff0c; Information Poor)。 &a…

侯捷C++面向对象高级编程(上)-2-构造函数

1.inline函数 2.访问级别 3.构造函数 4.重载

【力扣高频题】004.两个正序数组的中位数

------------------ 长文警告 ------------------ 4.两个正序数组的中位数 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O ( l o g ( m n ) ) O(log(mn)) O…

【期末速成】计算机操作系统 EP03 | 学习笔记

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;☀️☀️☀️2.1 考点五&#xff1a;进程的概念及特征2.1 考点六&#xff1a;进程的状态与切换 三、总结&#xff1a;&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f6…

Unix/Linux shell实用小程序1:生字本

前言 在日常工作学习中&#xff0c;我们会经常遇到一些不认识的英语单词&#xff0c;于时我们会打开翻译网站或者翻译软件进行查询&#xff0c;但是大部分工具没有生词本的功能&#xff0c;而有生字本的软件又需要注册登陆&#xff0c;免不了很麻烦&#xff0c;而且自己的数据…

linux-内存映射MMAP-lseek-dup-fifo-通信-IO多路复用

1、内存映射MMap&#xff1a; DMA&#xff1a; 可以用*/[]取代read和write&#xff1b; 限制&#xff1a; 1、文件大小固定不能改变&#xff1b;&#xff08;ftruncate&#xff09; 2、只能是磁盘文件&#xff1b; 3、建立映射之前先open mmap函数&#xff1a; mmap第一个…

GAN论文阅读笔记(10)—— High-fidelity GAN Inversion with Padding Space

论文&#xff1a;High-fidelity GAN Inversion with Padding Space paper&#xff1a;136750036.pdf (ecva.net) code&#xff1a;EzioBy/padinv: [ECCV 2022] PadInv: High-fidelity GAN Inversion with Padding Space (github.com) 关键词&#xff1a;GAN, GAN 反演 ( GAN I…

MobPush HarmonyOS NEXT 版本集成指南

开发工具&#xff1a;DevEco Studio 集成方式&#xff1a;在线集成 HarmonyOS API支持&#xff1a;> 11 集成前准备 注册账号 使用MobSDK之前&#xff0c;需要先在MobTech官网注册开发者账号&#xff0c;并获取MobTech提供的AppKey和AppSecret&#xff0c;详情可以点击查…

React实战学习(一)_棋盘设计

需求&#xff1a; 左上侧&#xff1a;状态左下侧&#xff1a;棋盘&#xff0c;保证胜利就结束 和 下过来的不能在下右侧&#xff1a;“时光机”,保证可以回顾&#xff0c;索引 语法&#xff1a; 父子之间属性传递&#xff08;props&#xff09;子父组件传递&#xff08;写法上&…

第十三章 常用类

一、包装类 1. 包装类的分类 &#xff08;1&#xff09;针对八种基本数据类型相应的引用类型—包装类 &#xff08;2&#xff09;有了类的特点&#xff0c;就可以调用类中的方法。 2. 包装类和基本数据的转换 jdk5 前的手动装箱和拆箱方式&#xff0c;装箱&#xff1a;基本…

使用SpringBoot整合filter

SpringBoot整合filter&#xff0c;和整合servlet类似&#xff0c;也有两种玩儿法 1、创建一个SpringBoot工程&#xff0c;在工程中创建一个filter过滤器&#xff0c;然后用注解WebFilter配置拦截的映射 2、启动类还是使用ServletComponentScan注解来扫描拦截器注解WebFilter 另…