【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 with
       Rect (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 in
      let n2 = eval e2 in
        if n2 <> 0 then n1 / n2
        else 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 =
    try
      a / b
    with 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 Fail
    else a / b;;
val div : int -> int -> int = <fun>
# div 10 5;;
- : int = 2
# div 10 0;;
Exception: Fail.
# try
    div 10 0
  with Fail -> 0;;
- : int = 0

0x02 模块(module)

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

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

module IntQueue = struct
  type 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

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

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

相关文章

基于盲信号处理的声音分离——最小化增益的ICA算法

基于最小化增益的ICA算法的算法是依据混合信号经过盲信号分离会产生一定的噪声&#xff0c;为了使得分离后的信号与原信号的比值最小时&#xff0c;叫做增益最小。当增益越小时&#xff0c;分离后噪声越小&#xff0c;分离信号越接近原信号&#xff0c;分离算法的效果越好。这是…

防止多次点击,vue的按钮上做简易的防抖节流处理

话不多说,上个视频,看看是不是你要的效果 防抖节流 1.创建一个directive.js // directive.js export default {install(Vue) {// 防重复点击(指令实现)Vue.directive(repeatClick, {inserted(el, binding) {el.addEventListener(click, () > {if (!el.disabled) {el.disabl…

【Stable Diffusion】AI绘画美女,搞副业,赚钱真香!(内附高质量美女提示词)

前言 今天就直接上干货&#xff0c;给铁子们上一些生成高质量美女的提示词&#xff0c;每一种美女类型都附有魔法咒语&#xff0c;可应用于midjourney和stablediffusion&#xff0c;直接复制即可。 话不多说&#xff0c;直接上图&#xff0c;上提示词&#xff0c;请欣赏&…

三大工作流引擎技术Activiti、Flowable、Camunda选型指南

文章目录 前言1 流程引擎发展历程2 流程引擎主要概念BPM (Business Process Management)BPMN (Business Process Model and Notation)CMMN (Case Management Model and Notation)DMN (Decision Model and Notation)事件&#xff08;Event&#xff09;顺序流&#xff08;Sequenc…

控制台扫雷(C语言实现)

目录 博文目的实现思路项目创建文件解释 具体实现判断玩家进行游戏还是退出扫雷棋盘的确定地图初始化埋雷玩家扫雷的实现雷判断函数 源码game.cgame.h扫雷.c 博文目的 相信不少人都学习了c语言的函数&#xff0c;循环&#xff0c;分支那我们就可以写一个控制台的扫雷小游戏来检…

【AI落地应用实战】如何让扫描工具更会思考——智能高清滤镜2.0实战测评

一、引言 在这个信息爆炸的数字化时代&#xff0c;扫描工具已经成为我们日常工作和学习中不可或缺的助手。最近&#xff0c;扫描全能王推出了革命性的“智能高清滤镜2.0”&#xff0c;本次更新后&#xff0c;智能高清滤镜能够智能识别并优化扫描过程中的各种问题。无论是光线不…

【乐吾乐2D可视化组态编辑器】图元外观编辑

1 节点的外观样式 角度&#xff1a;设置尖角与圆角&#xff0c;值的范围&#xff1a;0~1 旋转&#xff1a;设置图形的旋转角度 进度&#xff1a;任意封闭图形&#xff0c;都可以当进度条&#xff1a;矩形、圆、svg、封闭连线、或其他任意封闭图形&#xff0c;值的范围&#…

强化学习:值函数近似【Deep Q-Network,DQN,Deep Q-learning】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 第五章 强化学习实例分析:GridWorld…

【升压电子烟方案】DC-DC电源升压恒压芯片FP6277+全极低功耗霍尔MH251在电子烟中的应用

电子烟是一种新型烟草制品&#xff0c;由于其健康、环保和口感多样化的特点&#xff0c;逐渐受到了消费者的青睐。 升压芯片作为电子烟的核心组件之一&#xff0c;主要作用是将输入的电压升高至合适的工作电压&#xff0c;霍尔传感器控制电子烟的使用状态&#xff0c;以确保电子…

springboot系列七: Lombok注解,Spring Initializr,yaml语法

老韩学生 LombokLombok介绍Lombok常用注解Lombok应用实例代码实现idea安装lombok插件 Spring InitializrSpring Initializr介绍Spring Initializr使用演示需求说明方式1: IDEA创建方式2: start.spring.io创建 注意事项和说明 yaml语法yaml介绍使用文档yaml基本语法数据类型字面…

鸿蒙开发HarmonyOS NEXT (二) 熟悉ArkUI

一、构造函数 构造一个商品类Item&#xff0c;然后利用foreach函数循环渲染 class Item {name: stringimage: ResourceStrprice: numberdiscount: numberconstructor(name: string, image: ResourceStr, price: number, discount: number 0) {this.name name;this.image ima…

自动化测试小技巧之Airtest-Selenium和Excel的无缝协作

一、前言 之前在问卷以及Q群上有同学有提出过能否将网页上的一些数据通过Airtest去导出生成一份Excel&#xff0c;那么我们今天一起讨论一下&#xff0c;我们应该如何去实现&#xff0c;以及当我们获取的数据类型不同的时候&#xff0c;获取的方式该怎么随之调整&#xff1f; …

MySQL InnoDB支持几种行格式

数据库表的行格式决定了一行数据是如何进行物理存储的&#xff0c;进而影响查询和DML操作的性能。 在InnoDB中&#xff0c;常见的行格式有4种&#xff1a; 1、COMPACT&#xff1a;是MySQL 5.0之前的默认格式&#xff0c;除了保存字段值外&#xff0c;还会利用空值列表保存null…

如何使用git将本地文件夹提交至云效代码库

目录 引言 一、准备工作 二、配置Git用户信息 三、克隆代码库到本地 四、添加文件至Git仓库 五、推送更改至云效代码库 六、如何删除云效文件 七、注意事项 八、总结 引言 随着团队协作开发的日益普及&#xff0c;代码版本控制系统&#xff08;VCS&#xff09;如Git已…

java+mysql图书管理系统

完整代码地址 1.运行效果图 2.主要代码 2.1.连接数据库 package com.my.homework.utils;import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;public class JDBCUtils {public static Connection getConnection() throws Exception {…

力压GPT-4o!新王Claude 3.5 Sonnet来了,直接免费可用

如今&#xff0c;大模型领域更卷了&#xff01; 前脚 OpenAI 发布 GPT4o&#xff0c;硬控全场&#xff0c;后脚就被最大的竞争对手 Anthropic 超越了。 刚刚&#xff0c;Anthropic 发布了全新大模型 Claude 3.5 Sonnet&#xff0c;号称是迄今为止最智能的模型。 据介绍&#x…

CSS|05 继承性与优先级

继承性 一、继承性的特点&#xff1a; 1.外层元素身上的样式会被内层元素所继承 2.如果内层元素与外层元素身上的演示相同时&#xff0c;外层元素的样式会被内层元素所覆盖 二、关于继承性的问题 是不是所有样式都能被继承&#xff1f; 答&#xff1a;并不是所有样式能被继承…

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

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

Excel数据恢复,4个技巧挽救重要数据

在数字化时代&#xff0c;Excel表格已经成为我们工作、学习乃至生活中不可或缺的一部分。它们承载着我们的数据、计划、分析和决策&#xff0c;如同一张张承载着智慧与汗水的地图&#xff0c;指引我们前行。然而&#xff0c;当这些宝贵的数据意外丢失或被误删时&#xff0c;我们…

【Linux操作系统】进程地址空间与动态库加载

当系统执行一个依赖动态库的可执行程序时&#xff0c;系统不仅要将该可执行程序加载到内存中还要由加载器将动态库加载到内存中&#xff08;静态库没有&#xff09;&#xff0c;因此必须要让加载器知道该动态库的名称&#xff0c;系统会默认在/lib64路径下查找&#xff0c;解决…