面向程序员的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 r1 ← IO.mkRef #[1, 2, 3, 4, 5]let mut r2 ← IO.mkRef [1, 2, 3, 4, 5]
然后我们可以通过IO.Ref.get来获取值,通过IO.Ref.set来设置值。
let mut r1 ← IO.mkRef #[1, 2, 3, 4, 5]let arr1 ← r1.getIO.println arr1r1.set (arr1.push 6)IO.println (← r1.get)
输出如下:
#[1, 2, 3, 4, 5]
#[1, 2, 3, 4, 5, 6]
对于列表,我们也是同样做法:
let mut r2 ← IO.mkRef [1, 2, 3, 4, 5]let arr2 ← r2.getIO.println arr2r2.set (arr2 ++ [6])IO.println (← r2.get)
输出如下:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
小练习
下面我们做几个小练习,看看大家有没有适应Lean4的编程方式。
- 将一个Nat类型的列表转换成Int类型的列表
例:
let l13: List Nat := [1, 2, 3, 4, 5]let l14 := l13.map Int.ofNatIO.println l14
Int.ofNat是将Nat类型转换成Int类型的函数,所以我们不需要再定义一个新的函数,直接调用它就可以了。
- 由于List只能顺序访问,我们将其转化成数组,然后排序,最后再转化回List。
例:
def sortList (lst : List Nat) : List Nat :=let arr := List.toArray lst -- 将列表转换为数组let sortedArr := Array.qsort arr (. < .) -- 对数组进行排序Array.toList sortedArr -- 将排序后的数组转换回列表
- 实现一个函数,遍历二维数组
最简单的方法就是使用两个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来写一些简单的代码。
相关文章:
面向程序员的Lean 4教程(2) - 数组和列表
面向程序员的Lean 4教程(2) - 数组和列表 上一节我们介绍了Lean4的基本语法,因为大部分程序员都有一定的编程基础,所以并没有介绍过细。这一节我们介绍Lean4中的线性表结构:数组和列表,顺带复习一下上一节的流程控制等内容。 数…...
【C++高并发服务器WebServer】-7:共享内存
本文目录 一、共享内存1.1 shmget函数1.2 shmat1.3 shmdt1.4 shmctl1.5 ftok1.6 共享内存和内存映射的关联1.7 小demo 二、共享内存操作命令 一、共享内存 共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称…...
【力扣Hot 100】链表1
1. 相交链表 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始相交**:** !https://assets.leetcode-cn.com/aliyun-lc-upload/uplo…...
稀土抗菌剂:提升产品质量,保障公共健康
随着全球对抗菌技术需求的不断增长,传统的抗菌剂逐渐暴露出其局限性,包括耐药性、环境污染及副作用等问题。在此背景下,稀土抗菌剂作为一种新兴的抗菌材料,凭借其卓越的抗菌性能、环保特性以及应用多样性,正在成为各行…...
机器学习11-学习路径推荐
机器学习11-学习路径推荐 本文希望摒除AI学习商业宣传要素,推荐一条极简的AI学习路线!推荐内容均为在线免费内容,如果有条件可以咨询专业的培训机构! 文章目录 机器学习11-学习路径推荐[toc] 1-AI培训路线第一阶段 Python-人工智能…...
postgresql根据主键ID字段分批删除表数据
生产环境针对大表的处理相对比较麻烦。 方案1、直接truncate,可能会遇到系统卡主的情况,因为truncate的过程中会对表进行加锁,会导致数据不能正常的写入 方案2、创建一个同结构的表结构,rename旧表,不停业务rename表担…...
《边界感知的分而治之方法:基于扩散模型的无监督阴影去除解决方案》学习笔记
paper:Boundary-Aware Divide and Conquer: A Diffusion-Based Solution for Unsupervised Shadow Removal 目录 摘要 1、介绍 2、相关工作 2.1 阴影去除 2.2 去噪扩散概率模型(Denoising Diffusion Probabilistic Models, DDPM) 3、方…...
java后端之事务管理
Transactional注解:作用于业务层的方法、类、接口上,将当前方法交给spring进行事务管理,执行前开启事务,成功执行则提交事务,执行异常回滚事务 spring事务管理日志: 默认情况下,只有出现Runti…...
深入浅出 SQLSugar:快速掌握高效 .NET ORM 框架
SQLSugar 是一个高效、易用的 .NET ORM 框架,支持多种数据库(如 SQL Server、MySQL、PostgreSQL 等)。它提供了丰富的功能,包括 CRUD 操作、事务管理、动态表名、多表联查等,开发者可以通过简单的链式操作实现复杂的数…...
数据结构——概念与时间空间复杂度
目录 前言 一相关概念 1什么是数据结构 2什么是算法 二算法效率 1如何衡量算法效率的好坏 2算法的复杂度 三时间复杂度 1时间复杂度表示 2计算时间复杂度 2.1题一 2.2题二 2.3题三 2.4题四 2.5题五 2.6题六 2.7题七 2.8题八 四空间复杂度 1题一 2题二 3…...
浅谈在AI时代GIS的发展方向和建议
在AI时代,GIS(地理信息系统)的发展正经历着深刻的变革,随着人工智能技术的进步,GIS不再仅仅是传统的地图和空间数据处理工具,而是向更加智能化、自动化、精准化的方向发展。作为一名GIS开发工程师ÿ…...
牛客周赛 Round 78 A-C
A.时间表查询! 链接:https://ac.nowcoder.com/acm/contest/100671/A 来源:牛客网 题目描述 今天是2025年1月25日,今年的六场牛客寒假算法基础集训营中,前两场比赛已经依次于 20250121、20250123 举行;而…...
Java I/O 流介绍
Java学习资料 Java学习资料 Java学习资料 一、引言 在 Java 编程中,I/O(Input/Output)流是处理输入和输出操作的核心机制。它允许程序与外部设备(如文件、网络连接、键盘、显示器等)进行数据交互。通过使用 I/O 流&…...
linux 内核学习方向以及职位
### 学习路径 1. 基础阶段: - C语言高级特性 - 指针和内存管理 - 数据结构实现 - 位操作 - 多线程编程 - Linux系统编程 - 文件I/O操作 - 进程管理 - 信号处理 - IPC机制 - Socket编程 - 必备知识 - 操作系统原理 - 计算机体系结构 - …...
HTML-新浪新闻-实现标题-样式1
用css进行样式控制 css引入方式: --行内样式:写在标签的style属性中(不推荐) --内嵌样式:写在style标签中(可以写在页面任何位置,但通常约定写在head标签中) --外联样式…...
【电磁兼容】CE 传导骚扰
一。是什么? 传导骚扰是指电气或电子设备产生的不需要的电磁能量,这些能量通过导线或其他金属路径传播到其他设备或者系统中。这种类型的干扰通常发生在同一电源线路连接的不同装置之间,或者是共享相同布线系统的组件间。 传导骚扰可以分为两…...
能说说MyBatis的工作原理吗?
大家好,我是锋哥。今天分享关于【Redis为什么这么快?】面试题。希望对大家有帮助; 能说说MyBatis的工作原理吗? MyBatis 是一款流行的持久层框架,它通过简化数据库操作,帮助开发者更高效地与数据库进行交互。MyBatis…...
詳細講一下RN(React Native)中的列表組件FlatList和SectionList
1. FlatList 基礎使用 import React from react; import { View, Text, FlatList, StyleSheet } from react-native;export const SimpleListDemo: React.FC () > {// 1. 準備數據const data [{ id: 1, title: 項目 1 },{ id: 2, title: 項目 2 },{ id: 3, title: 項目 3…...
LabVIEW进行可靠性测试时有哪些常见的问题
在进行LabVIEW开发和测试时,尤其是用于可靠性测试,可能会遇到一些常见的问题。以下是一些常见问题及其解决方法: 1. 数据采集卡与硬件兼容性问题 问题描述:某些数据采集卡(DAQ)与硬件设备的兼容性问题可能…...
MFC程序设计(四)窗口创建机制
钩子函数 钩子属于win32技术,具有优先勾取消息的权利:当一个消息产生时,钩子勾取消息进行处理,然后消息才送回程序 接下来以一个勾取窗口创建消息的钩子为例进行讲解 钩子类型有键盘钩子,鼠标钩子,WH_CBT…...
【JavaEE进阶】Spring留言板实现
目录 🎍预期结果 🍀前端代码 🎄约定前后端交互接口 🚩需求分析 🚩接口定义 🌳实现服务器端代码 🚩lombok介绍 🚩代码实现 🌴运行测试 🎄前端代码实…...
Unity开发一个单人FPS游戏的教程总结
这个系列的前几篇文章介绍了如何从头开始用Unity开发一个FPS游戏,感兴趣的朋友可以回顾一下。这个系列的文章如下: Unity开发一个FPS游戏_unity 模仿开发fps 游戏-CSDN博客 Unity开发一个FPS游戏之二_unity 模仿开发fps 游戏-CSDN博客 Unity开发一个F…...
论文速读|Is Cosine-Similarity of Embeddings Really About Similarity?WWW24
论文地址: https://arxiv.org/abs/2403.05440 https://dl.acm.org/doi/abs/10.1145/3589335.3651526 bib引用: inproceedings{Steck_2024, series{WWW ’24},title{Is Cosine-Similarity of Embeddings Really About Similarity?},url{http://dx.doi.o…...
71.在 Vue 3 中使用 OpenLayers 实现按住 Shift 拖拽、旋转和缩放效果
前言 在前端开发中,地图功能是一个常见的需求。OpenLayers 是一个强大的开源地图库,支持多种地图源和交互操作。本文将介绍如何在 Vue 3 中集成 OpenLayers,并实现按住 Shift 键拖拽、旋转和缩放地图的效果。 实现效果 按住 Shift 键&#…...
PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(上.文章部分)
一、引言 1.1 研究背景与意义 在数字化时代,医疗行业正经历着深刻的变革,智能化技术的应用为其带来了前所未有的发展机遇。随着医疗数据的指数级增长,传统的医疗诊断和治疗方式逐渐难以满足现代医疗的需求。据统计,全球医疗数据量预计每年以 48% 的速度增长,到 2025 年将…...
250125-package
1. 定义 包就是文件夹,作用是在大型项目中,避免不同人的编写的java文件出现同名进而导致报错;想象一个场景,在一个根目录中,每一个人都有自己的一个java文件夹,他可以将自己编写的文件放在该文件夹里&…...
左右互博02-unidbg主动调用外层so函数
unidbg 代码 ` package com.koohairev.demo;import com.github.unidbg.AndroidEmulator; import com.github.unidbg.LibraryResolver; import com.github.unidbg.Module; import com.github.unidbg.Symbol; import com.github.unidbg.arm.backend.DynarmicFactory; import com.…...
FastExcel的使用
前言 FastExcel 是一款基于 Java 的开源库,旨在提供快速、简洁且能解决大文件内存溢出问题的 Excel 处理工具。它兼容 EasyExcel,提供性能优化、bug 修复,并新增了如读取指定行数和将 Excel 转换为 PDF 的功能。 FastExcel 的主要功能 高性…...
均值(信息学奥赛一本通-1060)
【题目描述】 给出一组样本数据,包含n个浮点数,计算其均值,精确到小数点后4位。 【输入】 输入有两行,第一行包含一个整数n(n小于100),代表样本容量;第二行包含n个绝对值不超过1000的…...
Redis实战(黑马点评)——关于缓存(缓存更新策略、缓存穿透、缓存雪崩、缓存击穿、Redis工具)
redis实现查询缓存的业务逻辑 service层实现 Overridepublic Result queryById(Long id) {String key CACHE_SHOP_KEY id;// 现查询redis内有没有数据String shopJson (String) redisTemplate.opsForValue().get(key);if(StrUtil.isNotBlank(shopJson)){ // 如果redis的数…...
