14.轻松入门Move: 集合(上)
这一章我们将讲解如何保存数据的集合。说到数据的集合首先想到的肯定是数组,Move标准库给我们提供了vector模块以支持数组类型。
数组
vector是一个可变长度的,任意类型的容器。跟其他语言的数组一样,使用索引访问,索引从0开始。
如何使用数组,在结构体章节讲解String类型讲过:
#![allow(unused)] fn main() { //struct <type name> <has abilities> struct String has copy, drop, store { bytes: vector<u8>, } }
String类型本质就是一个字节数组。vector使用了泛型来支持任何类型,所以在使用它的时候需要指定类型参数。
创建数组
我们可以使用empty方法,创建一个空数组
#![allow(unused)] fn main() { let arr = vector::empty<u64>(); }
或者创建一个长度为1的数组
#![allow(unused)] fn main() { let arr = vector::singleton<u64>(12); }
也可以使用[]创建数组
#![allow(unused)] fn main() { let arr = vector<u64>[1,2,3,4]; }
添加元素
-
在数组尾部插入一个元素
#![allow(unused)] fn main() { vector::push_back<u64>(&mut arr, 67); }
-
在数组指定位置插入一个元素
#![allow(unused)] fn main() { //在数组arr的第三个元素位置插入100 vector::insert<u64>(&mut arr, 100, 2); }
insert函数第三个参数,用于指定插入位置。如果插入位置已经超过数组长度将会报错;等于数组长度则在数组末尾插入元素;小于数组长度该位置的元素及后续元素均往后移一位。注意这里不是替换原位置元素,而是插入。
-
合并两个数组
#![allow(unused)] fn main() { public fun append<Element>(lhs: &mut vector<Element>, mut other: vector<Element>) }
将other数组合并到lhs数组。被合并数组必须传值,合并数组则传可变引用。
#![allow(unused)] fn main() { let mut arr1 = vector::singleton<u64>(1); vector::push_back(&mut arr1, 2); vector::push_back(&mut arr1, 3); let mut arr2 = vector::singleton<u64>(3); vector::push_back(&mut arr2, 4); vector::append(&mut arr2, arr1); print(&arr2); //打印结果:[ 3, 4, 1, 2, 3 ] }
合并完的数据,顺序是:lhs数组、other数组。如果有重复的值也会保留。
获取元素
-
弹出数组尾部元素
#![allow(unused)] fn main() { assert!(!vector::is_empty<u64>(&arr), 1); let item = vector::pop_back<u64>(&mut arr); }
注意,弹出后返回的是元素值,数组中不再包含该值,长度-1。在调用pop_back之前请确保数组不为空否则将会报错。
-
获取指定位置元素
与弹出不同,borrow和borrow_mut只是获取元素引用,并没有从数组中取出元素。
borrow是不可变引用,只用于读;如果要修改元素则使用borrow_mut
#![allow(unused)] fn main() { let mut arr1 = vector::singleton<u64>(1); vector::push_back(&mut arr1, 2); vector::push_back(&mut arr1, 3); //可变引用第一个元素 let item = vector::borrow_mut(&mut arr1, 0); *item = 2; print(&arr1); //输出结果:[ 2, 2, 3 ] }
交换位置
-
反转数组
#![allow(unused)] fn main() { let mut arr1 = vector::singleton<u64>(1); vector::push_back(&mut arr1, 2); vector::push_back(&mut arr1, 3); vector::reverse(&mut arr1); print(&arr1); //输出结果:[ 3, 2, 1 ] }
-
两个位置互换
#![allow(unused)] fn main() { let mut arr1 = vector::singleton<u64>(1); vector::push_back(&mut arr1, 2); vector::push_back(&mut arr1, 3); //位置2和3交换值 vector::swap(&mut arr1, 1, 2); print(&arr1); //输入结果:[1, 3, 2] }
删除元素
-
弹出末尾元素
- 这个在上面已经讲过,不再赘述
-
删除指定位置元素
#![allow(unused)] fn main() { let mut arr1 = vector::singleton<u64>(1); vector::push_back(&mut arr1, 2); vector::push_back(&mut arr1, 3); vector::remove(&mut arr1, 1); print(&arr1); //输出结果:[1,3] }
注意:在删除指定位置的元素后,后续元素会自动前移一位。
-
删除并使用末尾元素填充
#![allow(unused)] fn main() { let mut arr1 = vector::singleton<u64>(1); vector::push_back(&mut arr1, 2); vector::push_back(&mut arr1, 3); vector::swap_remove(&mut arr1, 0); print(&arr1); //输出结果:[3, 2] }
被删除的位置使用末尾元素填充,省去了前移元素的时间,如果元素顺序不重要,优选此方法。
-
删除空数组
#![allow(unused)] fn main() { assert!(is_empty(&arr1), 1); vector::destroy_empty(arr1); }
注意,请在调用destroy_empty之前确保数组是空的,否则会导致报错
其他
-
获取数组长度
#![allow(unused)] fn main() { let len = vector::length(&arr1); }
-
判断数组是否为空
#![allow(unused)] fn main() { assert!(vector::is_empty(&arr1), 1); }
-
根据值获取元素位置
#![allow(unused)] fn main() { public fun index_of<Element>(v: &vector<Element>, e: &Element): (bool, u64) }
#![allow(unused)] fn main() { let (exsits, index) = vector::index_of<u64>(&arr1, &item); }
注意第二个参数是传入元素的引用不是本身.返回两个字段,第一个代表是否存在,第二个代表元素位置.
-
是否包含某元素
#![allow(unused)] fn main() { let item: u64 = 2; let exists = vector::contains<u64>(&arr1, &item); }
vector模块具有丰富的函数,利用pop_back和push_back可以轻松实现栈, 利用insert和remove也能轻松实现队列.
优先级队列
优先级队列使用最大堆排序方法来对优先级进行排序.优先级高的先出列.如果我们需要有序的数据集合,就可以使用优先级队列.
最大堆是二叉树并且每个节点的值都大于或等于其子节点.也就是说根节点就是堆中最大值.最大堆排序的平均时间复杂度是O(nlogn)是一种比较优秀的排序方法.
我们以学生成绩排名为例来介绍优先级队列的功能,创建一个学生结构体和一个学校期末报告结构体.期末报告结构体负责保存学生信息和成绩,并按照成绩从高到低输入分数和学生的信息.
#![allow(unused)] fn main() { public struct Student has drop,store { name: String, score: u64, } public struct SchoolReport has drop{ report: PriorityQueue<Student>, } }
期末报告report字段,类型就是优先级队列.这里需要注意的是,Student结构体作为优先级队列中实体的值,必须要有drop和store能力.
期末出成绩后,往优先级队列中塞入成绩
#![allow(unused)] fn main() { public fun new(): SchoolReport{ let student1 = Student{ name: string::utf8(b"hanmeimei"), score: 89, }; let student2 = Student{ name: string::utf8(b"lilei"), score: 97, }; let p = vector<u64>[89,97]; let students = vector<Student>[student1, student2]; SchoolReport{ report: priority_queue::new<Student>(priority_queue::create_entries<Student>(p, students)), } } }
在创建优先级队列之前,我们需要调用create_entries方法创建实体数组.
塞入一个成绩为70分的学生:
#![allow(unused)] fn main() { public fun add_student(sr: &mut SchoolReport) { let student1 = Student{ name: string::utf8(b"libai"), score: 70, }; priority_queue::insert<Student>(&mut sr.report, 70, student1); } }
按照成绩从高到低排序并输出
#![allow(unused)] fn main() { public fun rank(sr: &mut SchoolReport) { while(true) { let (p, student) = priority_queue::pop_max<Student>(&mut sr.report); print(&p); print(&student.name); } } }
pop_max每次一定会输出堆内最大值,多次调用直到堆内无数据,即可实现对分数的排序.
了解更多Move内容:
- telegram: t.me/move_cn
- QQ群: 79489587