面向程序员的Lean 4教程(2) - 数组和列表

面向程序员的Lean 4教程(2) - 数组和列表

上一节我们介绍了Lean4的基本语法,因为大部分程序员都有一定的编程基础,所以并没有介绍过细。这一节我们介绍Lean4中的线性表结构:数组和列表,顺带复习一下上一节的流程控制等内容。

数组

创建数组

Lean4中的数组可以用#[]来进行初始化,也可以用Array.mk来创建。数组的元素类型可以是任意类型,但是所有元素的类型必须相同。

  let a1 : Array Nat := #[1, 2, 3, 4, 5]let a2 : Array Int := Array.mk [1, 2, 3, 4, 5]IO.println a1IO.println a2

输出如下:

#[1, 2, 3, 4, 5]
#[1, 2, 3, 4, 5]

访问数组元素

Lean4中的数组元素可以通过a[i]来访问,其中a是数组,i是索引。

  IO.println a1[0]IO.println a2[1]

我们也可以通过Array.get来访问数组元素。

如果想直接获取值,我们可以使用get!,如果可能为空,我们要使用get?

  IO.println a1.get! 0IO.println a2.get? 1

get?返回一个Option类型,我们可以通过match来处理。

  match a1.get? 10 with| none => IO.println "Not found"| some v => IO.println v

修改数组元素

Lean4中的数组元素是只读的,我们不能直接修改数组元素。如果想修改数组元素,我们可以使用Array.set!,它会返回一个新的数组。

  let a4 : Array Nat := Array.set! a1 0 10IO.println a4

获取数组长度

Lean4中的数组长度可以通过Array.size来获取:

  IO.println (Array.size a4)IO.println (a4.size)

拼接数组

Lean4中的数组可以通过Array.append来拼接:

  let a5 := Array.append a1 a4IO.println a5

输出如下:

#[1, 2, 3, 4, 5, 10, 2, 3, 4, 5]

数组切片

Lean4中的数组可以通过Array.extract来切片:

  let a6 := Array.extract a5 1 4IO.println a6

输出如下:

#[2, 3, 4]

数组反转

Lean4中的数组可以通过Array.reverse来反转:

  let a7 := Array.reverse a5IO.println a7

输出如下:

#[5, 4, 3, 2, 10, 5, 4, 3, 2, 1]

数组排序

Lean4中的数组可以通过Array.qsort来排序:

  let a8 := Array.qsort a5 (fun x y => x < y)IO.println a8

输出如下:

#[1, 2, 2, 3, 3, 4, 4, 5, 5, 10]

fun定义了一个匿名函数,x < y是函数体。

但是这样的写法不高级,在Lean4中我们可以使用洞来简化:

  let a8 := Array.qsort a5 (. < .)IO.println a8

fun这样的关键字省了,形式参数用.来表示,只突出了最重要关系判断的部分。是不是很高级?:)

查找数组是否包含某个元素

Lean4中的数组可以通过Array.contains来查找是否包含某个元素:

  let b2 := Array.contains a8 10IO.println b2

输出为true

查找符合条件的元素

如果只想找到一个符合条件的元素,我们可以使用Array.find?

  let a10 := Array.find? (. > 3) a8IO.println a10

输出为some 4

如果想找到所有符合条件的元素,我们可以使用Array.filter

  let a11 := Array.filter (. > 3) a8IO.println a11

输出为#[4, 4, 5, 5, 10]

像栈一样操作数组

Lean4中的数组可以通过Array.push来像栈一样操作:

  let a11 := a10.push 100IO.println a11

输出如下:

#[8, 10, 2, 4, 6, 8, 10, 12, 14, 16, 18, 100]

然后我们可以通过Array.pop来弹出栈顶元素,这样数组就变成了原来的数组:

  let a12 := a11.popIO.println a12

输出如下:

#[8, 10, 2, 4, 6, 8, 10, 12, 14, 16, 18]

列表

数组一般是在内存中连续存储的,而列表是在内存中不连续存储的。

可以通过[]来创建列表:

  let l1 : List Nat := [1, 2, 3, 4, 5]IO.println l1

输出如下:

[1, 2, 3, 4, 5]

除此之外,我们还可以使用List.range来创建:

  let l2 := List.range 10IO.println l2

输出如下:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

列表的连接

Lean4中的列表可以通过List.append来连接,但是更常用的是++运算符:

  let l3 := l1 ++ l2IO.println l3

输出如下:

[1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

获取列表长度

Lean4中的列表长度可以通过List.length来获取:

  IO.println (List.length l3)IO.println (l3.length)

输出如下:

15
15

列表的反转

同数组一样,Lean4中的列表可以通过List.reverse来反转:

  let l4 := l3.reverseIO.println l4

输出如下:

[9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 5, 4, 3, 2, 1]

列表的遍历

按命令式编程的思维,传统方式就是用for循环来遍历列表:

  for x in l5 doIO.println x

用函数式编程的方式,我们可以使用map来遍历列表。比如生成一个新的列表,每个元素都是原来的元素的2倍:

  let l5 := l3.map ( . * 2)IO.println l5

列表的切片

Lean4中的列表可以通过List.take来获取前n个元素:

  let l6 := l5.take 3IO.println l6

输出如下:

[2, 4, 6]

Lean4中的列表可以通过List.drop来删除前n个元素:

  let l7 := l5.drop 3IO.println l7

输出如下:

[8, 10, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

列表的折叠

折叠是函数式编程中的一个重要概念,它可以将一个列表中的元素通过某种规则合并成一个值。

最常用的折叠情况就是求和:

  let l8 := l7.foldl (. + .) 0IO.println l8

输出为108

我们同样可以使用foldr来从右边开始折叠,这次我们求积。但是我们的列表里有0,所以我们先用filter过滤掉0:

  let l9 := l7.filter (. > 0)IO.println (l9.foldr (. * .) 1)

列表转换成数组

Lean4中的列表可以通过List.toArray来转换成数组:

  let a10 := l9.toArrayIO.println a10

输出如下:

#[8, 10, 2, 4, 6, 8, 10, 12, 14, 16, 18]

列表去重

Lean4中的列表可以通过List.eraseDup来去重,也就是去掉重复的元素:

  let l10 := l5.eraseDupsIO.println l10

输出如下:

[2, 4, 6, 8, 10, 0, 12, 14, 16, 18]

列表的量词

Lean4中的列表可以通过List.all来判断是否所有元素都满足某个条件,例:

  let b3 := l5.all (. > 0)IO.println b3

我们知道,l5中含有元素0,所以输出为false

Lean4中的列表还可以通过List.any来判断是否有元素满足某个条件:

  let b4 := l5.any (. > 0)IO.println b4

因为除了0以外,l5中的所有元素都大于0,所以满足条件,输出为true

给列表的头部添加元素

可以使用构造符号::来给列表的头部添加元素,当然,是生成新的列表:

  let l11 := 100 :: l5IO.println l11

输出如下:

[100, 2, 4, 6, 8, 10, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

在头部添加元素的时间复杂度是O(1)。

在尾部添加元素的时间复杂度是O(n),就是我们之前介绍的++运算符。

可变的数组和列表

Lean4中的数组和列表是不可变的,如果想要可变的数组和列表,我们可以使用IO.Ref
注意,从IO.Ref获取值时,我们需要使用,不要写成:=

    let mut r1IO.mkRef #[1, 2, 3, 4, 5]let mut r2IO.mkRef [1, 2, 3, 4, 5]

然后我们可以通过IO.Ref.get来获取值,通过IO.Ref.set来设置值。

  let mut r1IO.mkRef #[1, 2, 3, 4, 5]let arr1r1.getIO.println arr1r1.set (arr1.push 6)IO.println (r1.get)

输出如下:

#[1, 2, 3, 4, 5]
#[1, 2, 3, 4, 5, 6]

对于列表,我们也是同样做法:

  let mut r2IO.mkRef [1, 2, 3, 4, 5]let arr2r2.getIO.println arr2r2.set (arr2 ++ [6])IO.println (r2.get)

输出如下:

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]

小练习

下面我们做几个小练习,看看大家有没有适应Lean4的编程方式。

  1. 将一个Nat类型的列表转换成Int类型的列表

例:

  let l13: List Nat := [1, 2, 3, 4, 5]let l14 := l13.map Int.ofNatIO.println l14

Int.ofNat是将Nat类型转换成Int类型的函数,所以我们不需要再定义一个新的函数,直接调用它就可以了。

  1. 由于List只能顺序访问,我们将其转化成数组,然后排序,最后再转化回List。

例:

def sortList (lst : List Nat) : List Nat :=let arr := List.toArray lst  -- 将列表转换为数组let sortedArr := Array.qsort arr (. < .)   -- 对数组进行排序Array.toList sortedArr  -- 将排序后的数组转换回列表
  1. 实现一个函数,遍历二维数组

最简单的方法就是使用两个for循环:

def traverse2DArray (arr : Array (Array Nat)) : IO Unit := dofor row in arr dofor elem in row doIO.print s!"{elem} "IO.println ""

当然也可以采用别的函数式的方法,或者递归的方法。

小结

本节我们不厌其烦地介绍了很多数组和列表的操作,在让大家熟悉这两种数据结构的同时,也是让大家进一步熟悉Lean4的基本编程方式。我们后面会继续深入,现在大家可以先练习一下用Lean4来写一些简单的代码。

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

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

相关文章

无人机如何自主侦察?UEAVAD:基于视觉的无人机主动目标探测与导航数据集

作者&#xff1a;Xinhua Jiang, Tianpeng Liu, Li Liu, Zhen Liu, and Yongxiang Liu 单位&#xff1a;国防科技大学电子科学学院 论文标题&#xff1a;UEVAVD: A Dataset for Developing UAV’s Eye View Active Object Detection 论文链接&#xff1a;https://arxiv.org/p…

【图文详解】lnmp架构搭建Discuz论坛

安装部署LNMP 系统及软件版本信息 软件名称版本nginx1.24.0mysql5.7.41php5.6.27安装nginx 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客: 关闭防火墙 systemctl stop firewalld &&a…

Ansible入门学习之基础元素介绍

一、Ansible目录结构介绍 1.通过rpm -ql ansible获取ansible所有文件存放的目录 有配置文件目录 /etc/ansible/ 执行文件目录 /usr/bin/ 其中 /etc/ansible/ 该文件目录的主要功能是 inventory主机信息配置&#xff0c;ansible工具功能配置。 ansible自身的配置文件…

git Bash通过SSH key 登录github的详细步骤

1 问题 通过在windows 终端中的通过git登录github 不再是通过密码登录了&#xff0c;需要本地生成一个密钥&#xff0c;配置到gihub中才能使用 2 步骤 &#xff08;1&#xff09;首先配置用户名和邮箱 git config --global user.name "用户名"git config --global…

矩阵的秩在机器学习中具有广泛的应用

矩阵的秩在机器学习中具有广泛的应用&#xff0c;主要体现在以下几个方面&#xff1a; 一、数据降维与特征提取 主成分分析&#xff08;PCA&#xff09;&#xff1a; PCA是一种常用的数据降维技术&#xff0c;它通过寻找数据中的主成分&#xff08;即最大方差方向&#xff09…

Windows Defender添加排除项无权限的解决方法

目录 起因Windows Defender添加排除项无权限通过管理员终端添加排除项管理员身份运行打开PowerShell添加/移除排除项的命令 起因 博主在打软件补丁时&#xff0c;遇到 Windows Defender 一直拦截并删除文件&#xff0c;而在 Windows Defender 中无权限访问排除项。尝试通过管理…

IDEA工具下载、配置和Tomcat配置

1. IDEA工具下载、配置 1.1. IDEA工具下载 1.1.1. 下载方式一 官方地址下载 1.1.2. 下载方式二 官方地址下载&#xff1a;https://www.jetbrains.com/idea/ 1.1.3. 注册账户 官网地址&#xff1a;https://account.jetbrains.com/login 1.1.4. JetBrains官方账号注册…

计算机网络之应用层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 05 应用层 在网上看到其他人做了相关笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 王道考研 计算机网络笔记 第六章&#xff1a;应用层_王道考研 应用层 笔记-CSDN博客 DNS&#x…

微信小程序date picker的一些说明

微信小程序的picker是一个功能强大的组件&#xff0c;它可以是一个普通选择器&#xff0c;也可以是多项选择器&#xff0c;也可以是时间、日期、省市区选择器。 官方文档在这里 这里讲一下date picker的用法。 <view class"section"><view class"se…

Pyecharts图表交互功能提升

在数据可视化中&#xff0c;交互功能可以极大地提升用户体验&#xff0c;让用户能够更加深入地探索数据。Pyecharts 提供了多种强大的交互功能&#xff0c;本篇将重点介绍如何使用缩略轴组件、配置图例交互&#xff0c;让我们的数据可视化作品更加生动有趣。 一、缩略轴组件使…

奇怪的单词(快速扩张200个单词)

这是一些非常奇怪的单词&#xff1a; screw n.螺丝&#xff1b;螺丝钉 screwdriver n.起子&#xff0c;螺丝刀&#xff0c;改锥 copulation n.连接 copulate a.配合的 bonk n.撞击&#xff1b;猛击 v.轻击&#xff1b;碰撞ebony n.黑檀couple n.夫妇blonde n.金发女郎intimacy…

Ubuntu20.04 深度学习环境配置(持续完善)

文章目录 常用的一些命令安装 Anaconda创建conda虚拟环境查看虚拟环境大小 安装显卡驱动安装CUDA安装cuDNN官方仓库安装 cuDNN安装 cuDNN 库验证 cuDNN 安装确认 CUDA 和 cuDNN 是否匹配&#xff1a; TensorRT下载 TensorRT安装 TensorRT 本地仓库配置 GPG 签名密钥安装 Tensor…

Android多语言开发自动化生成工具

在做 Android 开发的过程中&#xff0c;经常会遇到多语言开发的场景&#xff0c;尤其在车载项目中&#xff0c;多语言开发更为常见。对应多语言开发&#xff0c;通常都是在中文版本的基础上开发其他国家语言&#xff0c;这里我们会拿到中-外语言对照表&#xff0c;这里的工作难…

数据结构——堆(C语言)

基本概念&#xff1a; 1、完全二叉树&#xff1a;若二叉树的深度为h&#xff0c;则除第h层外&#xff0c;其他层的结点全部达到最大值&#xff0c;且第h层的所有结点都集中在左子树。 2、满二叉树&#xff1a;满二叉树是一种特殊的的完全二叉树&#xff0c;所有层的结点都是最…

const的用法

文章目录 一、C和C中const修饰变量的区别二、const和一级指针的结合const修饰的量常出现的错误是:const和一级指针的结合总结&#xff1a;const和指针的类型转换公式 三、const和二级指针的结合 一、C和C中const修饰变量的区别 C中&#xff1a;const必须初始化&#xff0c;叫常…

机器学习-线性回归(参数估计之经验风险最小化)

给定一组包含 &#x1d441; 个训练样本的训练集 我们希望能够 学习一个最优的线性回归的模型参数 &#x1d498; 现在我们来介绍线性回归的一种模型参数估计方法&#xff1a;经验风险最小化。 我们前面说过&#xff0c;对于标签 &#x1d466; 和模型输出都为连续的实数值&…

appium自动化环境搭建

一、appium介绍 appium介绍 appium是一个开源工具、支持跨平台、用于自动化ios、安卓手机和windows桌面平台上面的原生、移动web和混合应用&#xff0c;支持多种编程语言(python&#xff0c;java&#xff0c;Ruby&#xff0c;Javascript、PHP等) 原生应用和混合应用&#xf…

视频多模态模型——视频版ViT

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细解读多模态论文《ViViT: A Video Vision Transformer》&#xff0c;2021由google 提出用于视频处理的视觉 Transformer 模型&#xff0c;在视频多模态领域有…

使用Cline+deepseek实现VsCode自动化编程

不知道大家有没有听说过cursor这个工具&#xff0c;类似于AIVsCode的结合体&#xff0c;只要绑定chatgpt、claude等大模型API&#xff0c;就可以实现对话式自助编程&#xff0c;简单闲聊几句便可开发一个软件应用。 但cursor受限于外网&#xff0c;国内用户玩不了&#xff0c;…

【Linux】Linux编译器-g++、gcc、动静态库

只要积极创造&#xff0c;机遇无时不有&#xff1b;只要善于探索&#xff0c;真理无处不在。&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;Linux编译器-g、gcc •&#x1f330;1. 背景知识 •&#x1f330;2. gcc如何完成 •…