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

  • 💬 写在前面:本章我们将探讨 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.mfbz.cn/a/751549.html

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

相关文章

电脑开机之后,键盘鼠标需要重新插拔才能正常使用?

前言 小白平时修电脑修得多&#xff0c;总是会遇到各种各样的奇葩问题。这不&#xff0c;又有一位小伙伴来咨询&#xff1a;电脑开机之后&#xff0c;键盘鼠标都不能用&#xff0c;需要重新插拔一下才能正常使用。 啧啧啧&#xff0c;真的是很奇怪的问题&#xff0c;基本上没见…

OpenCV报错已解决:Vector析构异常OpencvAssert CrtlsValidHeapPointer

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 在使用OpenCV进行图像处理时&#xff0c;我们可能会遇到Vector析构异常OpencvAssert CrtlsValidHeapPointer的问题。本文将…

Kubernetes之 资源管理

系列文章目录 Kubernetes之 资源管理 文章目录 系列文章目录前言一、资源管理介绍二、YAML语言介绍 1.1.YAML语法&#xff1a;2.读入数据总结 一、资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 1. kub…

深度解析RocketMq源码-IndexFile

1.绪论 在工作中&#xff0c;我们经常需要根据msgKey查询到某条日志。但是&#xff0c;通过前面对commitLog分析&#xff0c;producer将消息推送到broker过后&#xff0c;其实broker是直接消息到达broker的先后顺序写入到commitLog中的。我们如果想根据msgKey检索一条消息无疑…

如何理解AKM?

关于Wi-Fi的加密认证过程&#xff0c;我们前面已经讲解&#xff1a;WLAN数据加密机制_tls加密wifi-CSDN博客 今天我们来理解下AKM&#xff0c;AKM&#xff08;Authentication and Key Management&#xff09;在Wi-Fi安全中是指认证和密钥管理协议。它是用于确定Wi-Fi网络中的认…

Linux学习第54天:Linux WIFI 驱动:蓝星互联

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 数字化、现代化的今天&#xff0c;随处的WIFI给与了大众极大的方便&#xff0c;也感受到了科技的力量。万物互联、无线互联越来越成为一个不可逆转的趋势。现在比较火…

ISP IC/FPGA设计-第一部分-SC130GS摄像头分析(0)

1.介绍 SC130GS是一款国产的Global shutter CMOS图像传感器&#xff0c;最高支持1280Hx1024V240fps的传输速率&#xff1b;SC130GS有黑白和彩色款&#xff0c;作为ISP开发选择彩色的&#xff0c;有效像素窗口为1288Hx1032V&#xff0c;支持复杂的片上操作&#xff0c;选择他理…

谈谈WebComponents | 前端开发

一、 源起 让我们以一个例子开始。 假设我们要做一个环形进度条&#xff0c;它可以&#xff1a; 1、根据进度数值的不同&#xff0c;计算出百分比&#xff0c;以渲染对应的角度值。 2、根据设置的进度不同&#xff0c;我们用不同的颜色加以区分。 3、在环的中间我们以动画递增的…

基于RabbitMQ的异步消息传递:发送与消费

引言 RabbitMQ是一个流行的开源消息代理&#xff0c;用于在分布式系统中实现异步消息传递。它基于Erlang语言编写&#xff0c;具有高可用性和可伸缩性。在本文中&#xff0c;我们将探讨如何在Python中使用RabbitMQ进行消息发送和消费。 安装RabbitMQ 在 Ubuntu 上安装 Rabbi…

wps的domain转为shp矢量

wps的namelist制作、python出图和转矢量 简介 wps&#xff08;WRF Preprocessing System&#xff09;是中尺度数值天气预报系统WRF(Weather Research and Forecasting)的预处理系统。 wps的安装地址在GitHub上&#xff1a;https://github.com/wrf-model/WPS 下载完成后&…

注册中心不知选哪个?Zookeeper、Eureka、Nacos、Consul和Etcd 5种全方位剖析对比

本文给大家讲解 5 种常用的注册中心&#xff0c;对比其流程和原理&#xff0c;无论是面试还是技术选型&#xff0c;都非常有帮助。 对于注册中心&#xff0c;在写这篇文章前&#xff0c;我其实只对 ETCD 有比较深入的了解&#xff0c;但是对于 Zookeeper 和其他的注册中心了解甚…

pytorch统计学分布

1、pytorch统计学函数 import torcha torch.rand(2,2) print(a) print(torch.sum(a, dim0)) print(torch.mean(a, dim0)) print(torch.prod(a, dim0))print(torch.argmax(a, dim0)) print(torch.argmin(a, dim0)) print(torch.std(a)) print(torch.var(a)) print(torch.median…

AI进阶指南第四课,大模型优缺点研究?

在上一篇文章中&#xff0c;我主要探讨了LM模型与企业级模型的融合。 但是&#xff0c;在文末对于具体的大模型优缺点只是简单地说明了一下&#xff0c;并不细致。 因此&#xff0c;在这一节&#xff0c;我将更为细致地说明一下大模型的优缺点。 一&#xff0c;隐私安全 将L…

Python输入与输出基础

Python输入与输出基础 引言 Python是一种非常直观且功能强大的编程语言&#xff0c;它允许用户轻松地处理输入和输出操作。无论是从用户那里获取数据&#xff0c;还是将结果展示给用户&#xff0c;Python都提供了简单易用的函数和方法。 一、输入数据 在Python中&#xff0c…

UWB:DS-TWR( Double-sided two-way ranging)双边测距公式推导:为啥是乘法?

UWB DS-TWR&#xff08; Double-sided two-way ranging&#xff09;双边测距为啥是乘法&#xff1f;&#xff1f; 公式&#xff1a; 我们先看单边 Single-Sided Two-Way Ranging (SS-TWR) 单边很好理解。 symmetric double-sided TWR (SDS-TWR)对称的双边测距 再看双边 Trou…

LeetCode热题100——最长连续序列

给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 class Solution(object):def longestConsecutive(self, nums):""":t…

【MAVEN学习 | 第2篇】Maven工程创建及核心功能

文章目录 一. 基于IDEA的Maven工程创建1.1 Maven工程GAVP属性&#xff08;1&#xff09;GroupID 格式&#xff08;2&#xff09;ArtifactID 格式&#xff08;3&#xff09;Version版本号格式&#xff08;4&#xff09;Packaging定义规则 1.2 IDEA构建Maven JavaSE工程1.3 IDEA构…

kettle使用手册 安装9.0版本 建议设置为英语

0.新建转换的常用组件 0. Generate rows 定义一个字符串 name value就是字符串的值 0.1 String operations 字段转大写 去空格 1. Json input 来源于一个json文件 1.json 或mq接收到的data内容是json字符串 2. Json output 定义Jsonbloc值为 data, 左侧Fieldname是数据库…

VS2022(Visual Studio 2022)最新安装教程

1、下载 1、下载地址 - 官网地址&#xff1a;下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux - 根据自己的电脑的 【操作系统】 灵活选择。 2、安装包 【此处为Windows系统安装包】 2、安装 1、打开软件 - 右击【以管理员身份打开】&#xff0c; 2、准备配置 …

昇思25天学习打卡营第03天|张量Tensor

何为张量&#xff1f; 张量&#xff08;Tensor&#xff09;是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数&#xff0c;这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 &#x1d45b;维空间内&#xff0c;有  &#x1d45b;&a…