【PL理论深化】(9) Ocaml 语言:自定义类型 | 异常处理 | 模块

  • 💬 写在前面:本章我们将继续介绍 OCaml 的基本特性,自定义类型、异常处理和模块。掌握了这些内容后,编写基本程序应该不会有太大困难。接下来的两节将学习函数式编程中常用的两种编程风格 —— 递归函数和高阶函数。

目录

0x00 自定义类型

0x01 异常处理(exception)

0x02 模块(module)


0x00 自定义类型

可以使用关键字 type 来定义新类型。

首先,可以利用 type 为已有类型赋予新的名称:

type var = string
type vector = float list
type matrix = float list listtype var = string
type vector = float list
type matrix = float list list

或者可以创建一个新的值集合并将其定义为类型。

例如,可以如下定义表示 "星期几" 集合的类型 days。

# type days = Mon | Tue | Wed | Thu | Fri | Sat | Sun;;
# Mon;;
- : days = Mon
# Tue;;
- : days = Tue

在关键字 type 后面写上要定义的类型名称 (days),在等号后面列出属于该类型的值,用 | 分隔。

Mon, . . . , Sun 被称为 构造函数 (constructor),分别表示 days 类型的不同值。

在 OCaml 中,类型名称以小写字母开头,而构造函数以大写字母开头。

定义了新类型之后,可以定义对该类型的值进行操作的函数。 

例如,可以定义一个函数 nextday,该函数接收一个星期几作为输入并返回下一个星期几。

如下所示:

# let nextday d =match d with| Mon -> Tue | Tue -> Wed | Wed -> Thu | Thu -> Fri| Fri -> Sat | Sat -> Sun | Sun -> Mon ;;
val nextday : days -> days = <fun>
# nextday Mon;;
- : days = Tue

也可以定义带有其他值作为参数的构造函数。

例如,可以定义一个类型 shape,其值可以是矩形或圆形:

# type shape = Rect of int * int | Circle of int;;
type shape = Rect of int * int | Circle of int

表示矩形的构造函数 Rect 被定义为具有宽度和高度的参数,

而表示圆形的构造函数 Circle 被定义为具有半径的参数。

属于这个定义类型的值的例子如下:

# Rect (2,3);;
- : shape = Rect (2, 3)
# Circle 5;;
- : shape = Circle 5

可以这样编写一个函数来计算上述定义的图形的面积:

# let area s =match s withRect (w,h) -> w * h| Circle r -> r * r * 3;;
val area : shape -> int = <fun>
# area (Rect (2,3));;
- : int = 6
# area (Circle 5);;
- : int = 75

在上面的例子中,为了方便起见,将圆周率的值近似为3。

也可以通过归纳法来定义类型。例如,之前我们讲过的整数列表集合:

\frac{}{nil}     \frac{l}{n\cdot l}\, \, n\in \mathbb{Z}

在 OCaml 中,可以如下定义上述整数列表集合:

# type intlist = Nil | Cons of int * intlist;;
type intlist = Nil | Cons of int * intlist

集合的名称 (类型) 称为 intlist,并定义了创建集合元素的两种方法。

首先是 Nil,它是表示空列表的构造子。

Cons 是一个构造子,它在给定列表的开头添加一个元素以创建新列表。

例如,可以创建如下列表:

# Nil;;
- : intlist = Nil
# Cons (1, Nil);;
- : intlist = Cons (1, Nil)
# Cons (1, Cons (2, Nil));;
- : intlist = Cons (1, Cons (2, Nil))

例如,Cons (1, Cons (2, Nil)) 表示列表 [1;2]

现在我们可以编写处理列表的函数了,计算列表长度的函数如下所示:

# let rec length l =match l with| Nil -> 0| Cons (_, l’) -> 1 + length l’;;
val length : intlist -> int = <fun>
# length (Cons (1, Cons (2, Nil)));;
- : int = 2

让我们尝试用 OCaml 数据类型来定义之前定义的整数表达式:

\frac{}{n}\in \mathbb{Z}     \frac{E_1\, \, \, \, E_2}{E_1-E_2}      \frac{E_1\, \, \, \, E_2}{E_1+E_2}     \frac{E_1\, \, \, \, E_2}{E_1*E_2}     \frac{E_1\, \, \, \, E_2}{E_1/E_2}

可以将上述语法结构定义如下:

type exp =Int of int| Minus of exp * exp| Plus of exp * exp| Mult of exp * exp| Div of exp * exp

例如,(1+2)*(3/3) 被表达为以下形式::

# Mult(Plus(Int 1, Int 2), Div(Int 3, Int 3));;
- : exp = Mult (Plus (Int 1, Int 2), Div (Int 3, Int 3))

表示整数表达式含义的归纳规则可以通过递归函数来实现:

# let rec eval exp =match exp with| Int n -> n| Plus (e1, e2) -> (eval e1) + (eval e2)| Mult (e1, e2) -> (eval e1) * (eval e2)| Minus (e1, e2) -> (eval e1) - (eval e2)| Div (e1, e2) ->let n1 = eval e1 inlet n2 = eval e2 inif n2 <> 0 then n1 / n2else raise (Failure "division by 0");;
val eval : exp -> int = <fun>
# eval (Mult (Plus (Int 1, Int 2),Div (Int 3, Int 3)));;
- : int = 3

这是直接将语义结构的定义转换为递归函数。

当除以 0 时,由于其含义未定义,因此引发了异常。

0x01 异常处理(exception)

在 OCaml 中,如果计算表达式导致 运行时错误 (runtime error) ,

例如尝试将某个数除以 0,会引发 Division_by_zero 异常:

# let div a b = a / b;;
val div : int -> int -> int = <fun>
# div 10 5;;
- : int = 2
# div 10 0;;
Exception: Division_by_zero.

要处理运行时发生的异常,可以使用 try ... with 结构:

# let div a b =trya / bwith Division_by_zero -> 0;;
val div : int -> int -> int = <fun>
# div 10 5;;
- : int = 2
# div 10 0;;
- : int = 0

可以使用关键字 exception 来定义并使用新的异常,如下所示:

# exception Fail;;
exception Fail
# let div a b =if b = 0 then raise Failelse a / b;;
val div : int -> int -> int = <fun>
# div 10 5;;
- : int = 2
# div 10 0;;
Exception: Fail.
# trydiv 10 0with Fail -> 0;;
- : int = 0

0x02 模块(module)

模块 (module) 是类型和值的集合。它们将相关功能组合在一起并隐藏其内部实现。

例如,我们可以像下面这样通过模块定义 队列 (queue) 数据结构。

module IntQueue = structtype t = int list
exception E
let empty = []
let enq q x = q @ [x]
let is_empty q = q = []
let deq q =match q with| [] -> raise E| h::t -> (h, t)
let rec print q =match q with| [] -> print_string "\n"| h::t -> print_int h; print_string " "; print t
end

这里实现了一个基于列表的队列,模块的用户可以不了解具体实现细节,

仍然可以像下面这样使用它:

let q0 = IntQueue.empty
let q1 = IntQueue.enq q0 1
let q2 = IntQueue.enq q1 2
let (_,q3) = IntQueue.deq q2
let _ = IntQueue.print q1
let _ = IntQueue.print q2
let _ = IntQueue.print q3

创建了一个队列,添加和删除了元素,然后输出了其状态。输出结果如下所示:

1
1 2
2

.

到目前为止,我们已经介绍了 OCaml 的基本特性。

掌握了这些内容后,编写基本程序应该不会有太大困难。

接下来的两节将学习函数式编程中常用的两种编程风格 —— 递归函数和高阶函数。

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

📜 参考资料 

- 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.rhkb.cn/news/362922.html

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

相关文章

python CSSE7030

1 Introduction In this assignment, you will implement a (heavily) simplified version of the video game ”Into The Breach”. In this game players defend a set of civilian buildings from giant monsters. In order to achieve this goal, the player commands a s…

基于51单片机的银行排队呼叫系统设计

一.硬件方案 本系统是以排队抽号顺序为核心&#xff0c;客户利用客户端抽号&#xff0c;工作人员利用叫号端叫号&#xff1b;通过显示器及时显示当前所叫号数&#xff0c;客户及时了解排队信息&#xff0c;通过合理的程序结构来执行排队抽号。电路主要由51单片机最小系统LCD12…

unity使用XR插件开发SteamVR项目,异常问题解决方法

一、unity使用XR插件开发SteamVR项目&#xff0c;运行后相机高度异常问题解决方法如下操作 &#xff08;一&#xff09;、开发环境 1、Unity 2021.3.15f 2、XR Interaction Toolkit Version 2.5.2 &#xff08;com.unity.xr.interaction.toolkit&#xff09; 3、OpenXR Pl…

ArmSoM-Sige7/5/1 和树莓派5规格比较

引言 在当今快速发展的嵌入式系统领域&#xff0c;选择一款性能强大、功能丰富的开发板对于项目的成功至关重要。本文将介绍并比较 Sige7、Sige5、Raspberry Pi 5 和 Sige1 这四款开发板的关键规格和特性&#xff0c;帮助开发者和爱好者选择最适合其需求的平台。 ArmSoM-Sige…

智慧校园-缴费管理系统总体概述

在构建现代化教育环境的过程中&#xff0c;智慧校园缴费管理系统脱颖而出&#xff0c;成为提升校园财务管理效率与服务质量的关键一环。缴费管理系统需要精心设计&#xff0c;通过科技力量&#xff0c;让原本繁琐的缴费流程变得简单快捷&#xff0c;同时增强家校之间的互动与信…

postman教程-22-Newman结合Jenkins执行自动化测试

上一小节我们学习了Postman Newman运行集合生成测试报告的方法&#xff0c;本小节我们讲解一下Postman Newman结合Jenkins执行自动化测试的方法。 在软件开发过程中&#xff0c;持续集成&#xff08;CI&#xff09;是一种实践&#xff0c;旨在通过自动化的测试和构建过程来频繁…

Transformers 安装与基本使用

文章目录 Github文档推荐文章简介安装官方示例中文情感分析模型分词器 Tokenizer填充 Padding截断 Truncation google-t5/t5-small使用脚本进行训练Pytorch 机器翻译数据集下载数据集格式转换 Github https://github.com/huggingface/transformers 文档 https://huggingface…

【Sklearn-LR驯化】一文搞懂分类基石模型-逻辑回归使用总结

【Sklearn-驯化】一文搞懂分类基石模型-逻辑回归使用总结 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档关注&#xf…

Transformer教程之Encoder-Decoder架构

在当今的自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Transformer已经成为不可或缺的模型。它以其高效的并行计算和卓越的性能在多个任务中占据了主导地位。在这篇文章中&#xff0c;我们将深入探讨Transformer的核心——Encoder-Decoder架构&#xff0c;帮助大家…

设计模式原则——接口隔离原则

设计模式原则 设计模式示例代码库地址&#xff1a; https://gitee.com/Jasonpupil/designPatterns 接口隔离原则 要求程序员尽量将臃肿庞大的接口拆分为更小的和更具体的接口&#xff0c;让接口中只包含客户感兴趣的方法接口隔离原则的目标是降低类或模块之间的耦合度&…

STM32将外部SDRAM空间作为系统堆(Heap)空间

概述 stm32可以外扩很大的sram&#xff0c;常见外部sram的初始化函数一般是c语言写的&#xff0c;默认写在main函数里面。stm32初始化首先进入汇编代码startup_stm32f429xx.s&#xff0c;在汇编代码中Reset_Handler&#xff08;复位中断服务程序&#xff09;里面先调用了Syste…

【Python机器学习】自动化特征选择——迭代特征选择

在单变量测试中&#xff0c;没有使用模型&#xff1b;在基于模型的选择中&#xff0c;使用单个模型来选择特征。而在迭代特征选择中&#xff0c;将会构造一系列模型&#xff0c;每个模型都使用不同数量的特征。有两种基本方法&#xff1a; 1、开始时没有特征&#xff0c;然后逐…

Django使用

一、安装Django 可以在创建好的虚拟环境中下载包 pip install Django3.2.20 -i https://pypi.tuna.tsinghua.edu.cn/simple 查看对应的下载好的内容 1、在Lib的site-packages第三方包里&#xff1a;这就是django框架源码 2、在scripts中有个 这是个工具&#xff0c;帮助创建d…

Java nio 的线程通信机制线程通信Pipe

Java的Pipe是一种新的线程通信机制&#xff0c;传统的线程通信可以是通过共享内存的方式&#xff0c;socket等方式&#xff0c;而Pipe是通过Java NIO 通信的方式实现共享内存&#xff0c;优点类似于go语言的管道 先上代码 public static void main(String[] args) throws IOEx…

Java网络编程(JavaWeb的基础)

Java网络编程&#xff08;JavaWeb的基础&#xff09; 文章目录 Java网络编程&#xff08;JavaWeb的基础&#xff09;前言一、网络编程概述1.1 软件架构&网络基础1.2 网络通信要素:IP/端口/通信协议1.3 传输层协议:tcp/udp 二、网络编程API2.1 InetAddress类2.2 Socket类&am…

DELL:利用大语言模型(LLM)生成评论与解释,革新虚假信息检测

ACL 2024 DELL: Generating Reactions and Explanations for LLM-Based Misinformation Detection https://arxiv.org/abs/2402.10426https://arxiv.org/abs/2402.10426 1.概述 大型语言模型(LLM)虽在诸多领域显示出色性能,但在直接应用于新闻真实性鉴别时,面临两大核心挑…

uboot基本使用网络命令和从服务器端下载linux内核启动

网络命令ip地址设置: setenv gmac_debug 0; setenv mdio_intf rgmii; setenv bootdelay 1; setenv ethaddr 00:xxxx:81:70; // mac地址 setenv ipaddr xxx; //开发板 IP 地址 setenv netmask 255.255.255.0; setenv gatewayip xxx.1; setenv serverip xxxx; //服…

潮玩手办盲盒前端项目模版的技术探索与应用案例

一、引言 在数字化时代&#xff0c;随着消费者对个性化和艺术化产品的需求日益增长&#xff0c;潮玩手办和盲盒市场逐渐崭露头角。为了满足这一市场需求&#xff0c;前端技术团队需要构建一个功能丰富、用户友好的在线平台。本文旨在探讨潮玩手办盲盒前端项目模版的技术实现&a…

图像处理Python库--图片裁剪、缩放、灰度图、圆角等

图像处理Python库 py-img-processor1. 安装2. 使用(Usage)2.1 运行配置2.2 图像处理处理函数图像处理参数为字符串图像处理参数为JSON 命令行提取图像主色调 py-img-processor Image editor using Python and Pillow. 依赖Pillow开发的Python库&#xff0c;用于图像编辑处理。…

第6章 复制

文章目录 前言1.配置1.1建立复制1.2断开复制1.3 安全性1.4 只读1.5 传输延迟 2. 拓扑2.1.一主一从结构2.2.一主多从结构2.3.树状主从结构 3.原理3.1复制过程 前言 复制功能&#xff0c;实现了相同数据的多个Redis副本。复制功能是高可用Redis的基础&#xff0c;满足故障恢复和…