当前位置: 首页 > news >正文

详解Rust标准库:Vec向量

查看本地官方文档

安装rust后运行

rustup doc

查看The Standard Library即可获取标准库内容

std::vec::Vec定义

Vec除了可以作为动态数组还可以模拟为一个栈,仅使用pushpop即可

Vec默认分配在堆上,对于一个容量为4,有两个元素ab的向量内存布局如下

            ptr      len  capacity+--------+--------+--------+| 0x0123 |      2 |      4 |+--------+--------+--------+|v
Heap   +--------+--------+--------+--------+|    'a' |    'b' | uninit | uninit |+--------+--------+--------+--------+

Vec定义

pub struct Vec<T, A: Allocator = Global> {// 存储了动态数组的元素数据,并且根据需要可以动态地增长或收缩以容纳更多或更少的元素buf: RawVec<T, A>,// 表示当前Vec中实际存储的元素数量len: usize,
}

RawVec:定义

pub(crate) struct RawVec<T, A: Allocator = Global> {// 这是一个Unique<T>类型的指针,指向分配的内存块中存储元素的起始位置。这个指针用于访问和操作存储在RawVec中的元素ptr: Unique<T>,// 代表RawVec的容量,容量通常会随着元素的添加而动态调整,当需要更多空间时,会进行内存重新分配以增加容量。同时,对于零大小类型(ZST),容量的处理方式有所不同,capacity()方法会在这种情况下返回usize::MAXcap: Cap,// 分配器负责分配和释放内存,以及管理内存的生命周期,通过这个分配器,RawVec可以根据需要动态地分配和调整内存空间来存储元素alloc: A,
}

每次进行自动分配容量的开销比预先分配容量大得多,所以预先分配容量可以减少分配的开销,建议使用with_capacity预先分配容量

方法

with_capacity:创建一个具有指定初始容量的Vec

fn main() {let mut v = Vec::with_capacity(5);v.push(1);v.push(2);println!("Capacity: {}", v.capacity()); // 输出初始容量 5,目前只使用了部分容量// Capacity: 5
}

from_raw_parts:从给定的原始指针和长度创建一个Vec,使用时需要非常小心,确保内存管理正确

  • ptr必须使用全局分配器进行分配,例如通过alloc::alloc函数
  • T需要和ptr的对齐方式相同。(不那么严格的对齐是不够的,对齐确实需要相等才能满足dealloc要求,即内存必须以相同的布局分配和释放。)
  • T的大小乘以容量(即分配的大小(以字节为单位)需要与指针分配的大小相同。(因为与对齐类似,必须以相同的布局大小调用dealloc。)
  • Length必须小于或等于capacity容量
  • 第一个Length值必须正确初始化T类型的值
  • 容量需要是指针分配的容量
  • 分配的字节大小必须不大于isize::MAX
use std::ptr;
use std::mem;
fn main() {let v = vec![1, 2, 3];let mut v = mem::ManuallyDrop::new(v);let p = v.as_mut_ptr();let len = v.len();let cap = v.capacity();unsafe {// 覆盖内存for i in 0..len {ptr::write(p.add(i), 4 + i);}// 将内存重新构建为Veclet rebuilt = Vec::from_raw_parts(p, len, cap);println!("{:?}", rebuilt);// [4, 5, 6]}
}

capacity:返回Vec的当前容量

fn main() {let mut v = Vec::with_capacity(5);v.push(1);v.push(2);println!("Capacity: {}", v.capacity()); // 输出初始容量 5,目前只使用了部分容量// Capacity: 5
}

reserve:增加Vec的容量,确保至少可以容纳指定数量的更多元素

fn main() {let mut v: Vec<i32> = Vec::new();v.reserve(10);println!("Capacity after reserve: {}", v.capacity());// Capacity after reserve: 10
}

reserve_exact:精确地将Vec的容量调整为指定值,容量为vec.len()+addtitional

fn main() {let mut v = Vec::new();v.push(1);v.push(2);v.reserve_exact(5);println!("Capacity after reserve_exact: {}", v.capacity());// Capacity after reserve_exact: 7
}

try_reserve:尝试增加Vec的容量,如果无法增加则返回false

fn main() {let mut v: Vec<i32> = Vec::new();if v.try_reserve(1).is_ok() {println!("Reserved successfully");// Reserved successfully} else {println!("Failed to reserve");}
}

try_reserve_exact:尝试将Vec的容量精确调整为指定值,如果无法调整则返回false

fn main() {let mut v: Vec<i32> = Vec::new();if v.try_reserve_exact(5).is_ok() {println!("Reserved exactly successfully");// Reserved exactly successfully} else {println!("Failed to reserve exactly");}
}

shrink_to_fit:将Vec的容量调整为与当前长度相等,释放多余的内存

fn main() {let mut v = Vec::with_capacity(10);v.push(1);v.push(2);v.shrink_to_fit();println!("Capacity after shrink_to_fit: {}", v.capacity());// Capacity after shrink_to_fit: 2
}

shrink_to:尽可能将Vec的容量缩小为指定最小容量,释放多余的内存

fn main() {let mut v = Vec::with_capacity(10);v.push(1);v.push(2);v.shrink_to(1);println!("Capacity after shrink_to_fit: {}", v.capacity());// Capacity after shrink_to_fit: 2
}

into_boxed_slice:将Vec转换为Box<[T]>,所有权转移

fn main() {let v = vec![1, 2, 3];let boxed_slice = v.into_boxed_slice();println!("{}", boxed_slice[0]);// 1
}

truncate:截断Vec,使其长度不超过指定值

fn main() {let mut v = vec![1, 2, 3, 4, 5];v.truncate(3);println!("Truncated vector: {:?}", v);// Truncated vector: [1, 2, 3]
}

as_slice:返回Vec的不可变切片引用

fn main() {let v = vec![1, 2, 3];let slice = v.as_slice();for item in slice {println!("{}", item);}// 1// 2// 3
}

as_mut_slice:返回Vec的可变切片引用

fn main() {let mut v = vec![1, 2, 3];let slice = v.as_mut_slice();slice[0] = 4;println!("Modified vector: {:?}", v);// Modified vector: [4, 2, 3]
}

as_ptr:返回指向向量缓冲区的原始指针或悬空的原始指针

fn main() {let x = vec![1, 2, 4];let x_ptr = x.as_ptr();unsafe {for i in 0..x.len() {// 将指针加i即右移i位,然后解引用,得到对应位置的元素1,2,4// 1按位左移相当于*2assert_eq!(*x_ptr.add(i), 1 << i);}}
}

as_mut_ptr:返回指向Vec缓冲区的不安全可变指针,或悬空的指针

fn main() {let size = 4;let mut x: Vec<i32> = Vec::with_capacity(size);let x_ptr = x.as_mut_ptr();// 初始化原始指针并设置长度unsafe {for i in 0..size {*x_ptr.add(i) = i as i32;}x.set_len(size);}println!("{:?}", x);// [0, 1, 2, 3]
}

set_len:低级操作,用于修改向量长度

  • new_len必须小于或等于 capacity()
  • 必须初始化元素
fn main() {let size = 4;let mut x: Vec<i32> = Vec::with_capacity(size);let x_ptr = x.as_mut_ptr();// 初始化原始指针并设置长度unsafe {for i in 0..size {*x_ptr.add(i) = i as i32;}x.set_len(size);}println!("{:?}", x);// [0, 1, 2, 3]
}

swap_remove:从Vec中删除一个元素并返回它,被删除的元素位置将被Vec的最后一个元素替换,不保留其余元素的顺序,如果需要保留元素顺序请使用remove

fn main() {let mut v = vec!["foo", "bar", "baz", "qux"];let _ = v.swap_remove(1);println!("{:?}", v);// ["foo", "qux", "baz"]
}

insert:在指定位置插入一个元素

fn main() {let mut v = vec![1, 2, 3];v.insert(1, 4);println!("Inserted vector: {:?}", v);// Inserted vector: [1, 4, 2, 3]
}

remove:移除指定位置的元素并返回它,同时调整向量的长度

fn main() {let mut v = vec![1, 2, 3];let removed = v.remove(1);println!("Removed element: {}", removed);// Removed element: 2println!("After removal: {:?}", v);// After removal: [1, 3]
}

retain:保留满足给定谓词的元素,删除不满足的元素

fn main() {let mut v = vec![1, 2, 3, 4];v.retain(|&x| x % 2 == 0);println!("Retained vector: {:?}", v);// Retained vector: [2, 4]
}

retain_mut:保留满足给定谓词的元素,删除不满足的元素,并且可以在过程中修改元素

fn main() {let mut v = vec![1, 2, 3, 4];v.retain_mut(|x| {if *x % 2 == 0 {*x *= 2;true} else {false}});println!("After retain_mut: {:?}", v);// After retain_mut: [4, 8]
}

dedup_by_key:根据自定义的键提取函数去除连续重复的元素

fn main() {let mut vec = vec![10, 20, 21, 30, 20];vec.dedup_by_key(|i| *i / 10);println!("{:?}", vec);// [10, 20, 30, 20]
}

dedup_by:使用自定义的比较函数去除连续重复的元素

fn main() {let mut v = vec![1, 2, 2, 3];v.dedup_by(|a, b| a == b);println!("After dedup_by: {:?}", v);// After dedup_by: [1, 2, 3]
}

push:向Vec的末尾添加一个元素

fn main() {let mut v = Vec::new();v.push(1);v.push(2);println!("Pushed vector: {:?}", v);// Pushed vector: [1, 2]
}

pop:移除并返回Vec的末尾元素,如果Vec为空则返回None

fn main() {let mut v = vec![1, 2, 3];if let Some(item) = v.pop() {println!("Popped item: {}", item);// Popped item: 3}println!("After pop: {:?}", v);// After pop: [1, 2]
}

append:将另一个Vec的内容追加到当前Vec的末尾

fn main() {let mut v1 = vec![1, 2];let mut v2 = vec![3, 4];v1.append(&mut v2);println!("Appended vector: {:?}", v1);// Appended vector: [1, 2, 3, 4]
}

drain:移除并返回一个范围的元素,返回一个迭代器

fn main() {let mut v = vec![1, 2, 3, 4];let drained = v.drain(1..3);println!("Drained elements: {:?}", drained.collect::<Vec<_>>());// Drained elements: [2, 3]println!("After drain: {:?}", v);// After drain: [1, 4]
}

clear:移除Vec中的所有元素

fn main() {let mut v = vec![1, 2, 3];v.clear();println!("Cleared vector: {:?}", v);// Cleared vector: []
}

len:返回Vec中元素的数量

fn main() {let v = vec![1, 2, 3];println!("Length of vector: {}", v.len());// Length of vector: 3
}

is_empty:判断Vec是否为空

fn main() {let v: Vec<i32> = Vec::new();println!("Is vector empty? {}", v.is_empty());// Is vector empty? truelet v2 = vec![1];println!("Is vector empty? {}", v2.is_empty());// Is vector empty? false
}

split_off:在指定位置将Vec分割成两个部分,返回后半部分,原向量变为前半部分

fn main() {let mut v = vec![1, 2, 3, 4];let second_half = v.split_off(2);println!("First half: {:?}", v);// First half: [1, 2]println!("Second half: {:?}", second_half);// Second half: [3, 4]
}

resize_with:调整Vec的大小为指定大小,使用给定的闭包来填充新添加的元素

fn main() {let mut v = vec![1, 2];v.resize_with(4, || 0);println!("After resize_with: {:?}", v);// After resize_with: [1, 2, 0, 0]
}

leak:将Vec的所有权转移到堆上而不进行清理,防止向量在超出作用域时被释放。通常用于需要手动管理内存的情况


spare_capacity_mut:返回向量当前未使用的容量。通常用于了解向量内部的内存分配情况


resize:调整Vec的长度为指定大小,如果新长度大于当前长度,新添加的元素使用默认值填充

fn main() {let mut v = vec![1, 2];v.resize(4, 0);println!("Resized vector: {:?}", v);// Resized vector: [1, 2, 0, 0]
}

extend_from_slice:从一个切片中扩展Vec,将切片中的元素添加到Vec的末尾

fn main() {let mut v = vec![1, 2];let slice = &[3, 4];v.extend_from_slice(slice);println!("Extended vector: {:?}", v);// Extended vector: [1, 2, 3, 4]
}

extend_from_within:从向量自身的一部分扩展向量

fn main() {let mut v = vec![1, 2, 3, 4];v.extend_from_within(1..3);println!("After extend_from_within: {:?}", v);// After extend_from_within: [1, 2, 3, 4, 2, 3]
}

into_flattened:将包含迭代器的向量转换为一个扁平的迭代器

fn main() {let v = vec![vec![1, 2], vec![3, 4]];let flattened = v.into_iter().flatten().collect::<Vec<_>>();println!("Flattened vector: {:?}", flattened);// Flattened vector: [1, 2, 3, 4]
}

dedup:删除Vec中连续重复的元素,只保留第一个出现的元素

fn main() {let mut v = vec![1, 1, 2, 2, 3];v.dedup();println!("Deduped vector: {:?}", v);// Deduped vector: [1, 2, 3]
}

splice:创建一个拼接迭代器,用给定迭代器替换载体中的指定范围,并生成删除的项

fn main() {let mut v = vec![1, 2, 3, 4];let new = [7, 8, 9];let u: Vec<_> = v.splice(1..3, new).collect();println!("{:?}", u);// [2, 3]println!("{:?}", v);// [1, 7, 8, 9, 4]
}

Deref上的方法

as_flattened:将切片中包含的切片扁平化为单个切片

fn main() {let v1 = [[1, 2, 3],[4, 5, 6],].as_flattened();println!("{:?}", v1);// [1, 2, 3, 4, 5, 6]
}

as_flattened_mut将切片中包含的切片扁平化为可变切片

fn main() {fn add_5_to_all(slice: &mut [i32]) {for i in slice {*i += 5;}}let mut array = [[1, 2, 3],[4, 5, 6],[7, 8, 9],];array.as_flattened_mut();add_5_to_all(array.as_flattened_mut());println!("{:?}", array);// [[6, 7, 8], [9, 10, 11], [12, 13, 14]]
}

is_ascii:判断Vec中的所有字符是否都是 ASCII 字符

fn main() {let v = vec![1, 2, 3];println!("Is ASCII? {}", v.is_ascii());// Is ASCII? true
}

make_ascii_uppercase:就地将切片转换为ASCII大写等效项
make_ascii_lowercase:就地将切片转换为ASCII小写等效项

fn main() {let mut v = vec![97, 98, 65, 66, 200];println!("Original vector: {:?}", v);// Original vector: [97, 98, 65, 66, 200]v.make_ascii_uppercase();println!("After make_ascii_uppercase: {:?}", v);// After make_ascii_uppercase: [65, 66, 65, 66, 200]v.make_ascii_lowercase();println!("After make_ascii_lowercase: {:?}", v);// After make_ascii_lowercase: [97, 98, 97, 98, 200]
}

escape_ascii:返回一个迭代器,该迭代器生成此切片的转义版本, 将其视为 ASCII 字符串

fn main() {let s = b"0\t\r\n'\"\\\x9d";let escaped = s.escape_ascii().to_string();println!("{}", escaped);// 0\t\r\n\'\"\\\x9d
}

len:返回切片中的元素数

fn main() {let a = [1, 2, 3];println!("{:?}", a.len());// 3
}

is_empty:判断切片是否为空

fn main() {let b: &[i32] = &[];println!("Is emoty: {}", b.is_empty());// Is emoty: true
}

first:返回切片的第一个元素

fn main() {let v = [10, 40, 30];println!("{}", v.first().unwrap());// 10
}

first_mut:返回指向slice的第一个元素的可变指针

fn main() {let x = &mut [0, 1, 2];if let Some(first) = x.first_mut() {*first = 5;}println!("{:?}", x);// [5, 1, 2]
}

split_first:将Vec分割为第一个元素和剩余部分,如果向量为空,则返回None

fn main() {let v = vec![1, 2, 3];if let Some((first, rest)) = v.split_first() {println!("First element: {}", first);// First element: 1println!("Rest: {:?}", rest);// Rest: [2, 3]}
}

split_last:将Vec分割为最后一个元素和前面的部分,如果向量为空,则返回None

fn main() {let v = vec![1, 2, 3];if let Some((front, last)) = v.split_last() {println!("Front: {:?}", front);// Front: 3println!("Last element: {:?}", last);// Last element: [1, 2]}
}

get:通过索引安全地访问Vec中的元素,如果索引超出范围,则返回None

fn main() {let v = vec![1, 2, 3];if let Some(item) = v.get(1) {println!("Element at index 1: {}", item);// Element at index 1: 2}
}

get_mut:通过索引安全地获取Vec中元素的可变引用,如果索引超出范围,则返回None

fn main() {let mut v = vec![1, 2, 3];if let Some(item) = v.get_mut(1) {*item = 4;}println!("Modified vector: {:?}", v);// Modified vector: [1, 4, 3]
}

get_unchecked:返回对元素或子切片的引用,不进行边界检查

fn main() {let x = &[1, 2, 4];unsafe {println!("{}", x.get_unchecked(1));// 2}
}

get_unchecked_mut:返回对元素或子切片的可变引用,不执行边界检查

fn main() {let x = &mut [1, 2, 4];unsafe {let elem = x.get_unchecked_mut(1);*elem = 13;}println!("{:?}", x);// [1, 13, 4]
}

swap:交换Vec中两个指定索引的元素

fn main() {let mut v = vec![1, 2, 3];v.swap(0, 2);println!("Swapped vector: {:?}", v);// Swapped vector: [3, 2, 1]
}

reverse:反转Vec中元素的顺序

fn main() {let mut v = vec![1, 2, 3];v.reverse();println!("Reversed vector: {:?}", v);// Reversed vector: [3, 2, 1]
}

iter:返回一个不可变的迭代器,用于遍历Vec中的元素

fn main() {let v = vec![1, 2, 3];for item in v.iter() {println!("{}", item);}// 1// 2// 3
}

iter_mut:返回一个可变的迭代器,用于遍历Vec中的元素

fn main() {let mut v = vec![1, 2, 3];for item in v.iter_mut() {*item *= 2;}println!("Modified vector: {:?}", v);// Modified vector: [2, 4, 6]
}

windows:返回一个迭代器,产生固定大小的窗口切片

fn main() {let v = vec![1, 2, 3, 4];for window in v.windows(2) {println!("Window: {:?}", window);}// Window: [1, 2]// Window: [2, 3]// Window: [3, 4]
}

chunks:将Vec分割成不重叠的子切片,每个子切片的大小尽可能接近但不超过指定的大小

fn main() {let v = vec![1, 2, 3, 4, 5];for chunk in v.chunks(2) {println!("Chunk: {:?}", chunk);}// Chunk: [1, 2]// Chunk: [3, 4]// Chunk: [5]
}

chunks_mut:与chunks类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];for chunk in v.chunks_mut(2) {for item in chunk {*item *= 2;}}println!("Modified vector with chunks_mut: {:?}", v);// Modified vector with chunks_mut: [2, 4, 6, 8, 10]
}

chunks_exact:将Vec分割成大小完全为指定大小的不重叠子切片,如果向量的长度不是指定大小的整数倍,则最后一个子切片可能小于指定大小。如果向量的长度不能被指定大小整除,则返回一个空迭代器

fn main() {let v = vec![1, 2, 3, 4];for chunk in v.chunks_exact(2) {println!("Exact chunk: {:?}", chunk);}// Exact chunk: [1, 2]// Exact chunk: [3, 4]
}

chunks_exact_mut:与chunks_exact类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4];for chunk in v.chunks_exact_mut(2) {for item in chunk {*item *= 2;}}println!("Modified vector with chunks_exact_mut: {:?}", v);// Modified vector with chunks_exact_mut: [2, 4, 6, 8]
}

rchunks:从向量的末尾开始分割成不重叠的子切片,每个子切片的大小尽可能接近但不超过指定的大小

fn main() {let v = vec![1, 2, 3, 4, 5];for chunk in v.rchunks(2) {println!("Reverse chunk: {:?}", chunk);}// Reverse chunk: [4, 5]// Reverse chunk: [2, 3]// Reverse chunk: [1]
}

rchunks_mut:与rchunks类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];for chunk in v.rchunks_mut(2) {for item in chunk {*item *= 2;}}println!("Modified vector with rchunks_mut: {:?}", v);// Modified vector with rchunks_mut: [2, 4, 6, 8, 10]
}

rchunks_exact:从向量的末尾开始分割成大小完全为指定大小的不重叠子切片,如果向量的长度不是指定大小的整数倍,则最后一个子切片可能小于指定大小。如果向量的长度不能被指定大小整除,则返回一个空迭代器

fn main() {let v = vec![1, 2, 3, 4, 4, 5];for chunk in v.rchunks_exact(2) {println!("Reverse exact chunk: {:?}", chunk);}
}
// Reverse exact chunk: [4, 5]
// Reverse exact chunk: [3, 4]
// Reverse exact chunk: [1, 2]

rchunks_exact_mut:与rchunks_exact类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4];for chunk in v.rchunks_exact_mut(2) {for item in chunk {*item *= 2;}}println!("Modified vector with rchunks_exact_mut: {:?}", v);// Modified vector with rchunks_exact_mut: [2, 4, 6, 8]
}

chunk_by:根据给定的谓词将Vec分割成不重叠的子切片,只要谓词对连续的元素返回相同的值,这些元素就会被分到同一个子切片中

fn main() {let v = vec![1, 2, 3, 4, 5];let chunks = v.chunk_by(|a, b| a % 2 == b % 2);for chunk in chunks {println!("Chunk by predicate: {:?}", chunk);}// Chunk by predicate: [1]// Chunk by predicate: [2]// Chunk by predicate: [3]// Chunk by predicate: [4]// Chunk by predicate: [5]
}

chunk_by_mut:与chunk_by类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];let chunks = v.chunk_by_mut(|a, b| a % 2 == b % 2);for chunk in chunks {for item in chunk {*item *= 2;}}println!("Modified vector with chunk_by_mut: {:?}", v);// Modified vector with chunk_by_mut: [2, 4, 6, 8, 10]
}

split_at:将Vec在指定位置分割成两个部分,返回一个包含两个切片的元组

fn main() {let v = vec![1, 2, 3, 4, 5];let (left, right) = v.split_at(2);println!("Left: {:?}", left);// Left: [1, 2]println!("Right: {:?}", right);// Right: [3, 4, 5]
}

split_at_mut:与split_at类似,但返回可变的两个切片

fn main() {let mut v = vec![1, 2, 3, 4, 5];let (left, right) = v.split_at_mut(2);for item in left.iter_mut() {*item *= 2;}println!("Modified left and right: {:?}, {:?}", left, right);// Modified left and right: [2, 4], [3, 4, 5]
}

split_at_unchecked:通过索引不安全地将Vec在指定位置分割成两个部分,不进行边界检查。这是一个危险的操作,可能导致未定义行为如果索引超出范围

fn main() {let v = vec![1, 2, 3, 4, 5];let (left, right) = unsafe { v.split_at_unchecked(2) };println!("Unchecked split left: {:?}", left);// Unchecked split left: [1, 2]println!("Unchecked split right: {:?}", right);// Unchecked split right: [3, 4, 5]
}

split_at_mut_unchecked:与split_at_unchecked类似,但返回可变的两个切片。同样是危险的操作,不进行边界检查

fn main() {let mut v = vec![1, 2, 3, 4, 5];let (left, right) = unsafe { v.split_at_mut_unchecked(2) };for item in left.iter_mut() {*item *= 2;}println!("Modified unchecked left and right: {:?}, {:?}", left, right);// Modified unchecked left and right: [2, 4], [3, 4, 5]
}

split_at_checked:通过索引安全地将Vec在指定位置分割成两个部分,如果索引超出范围则返回None

fn main() {let v = vec![1, 2, 3, 4, 5];if let Some((left, right)) = v.split_at_checked(2) {println!("Checked split left: {:?}", left);// Checked split left: [1, 2]println!("Checked split right: {:?}", right);// Checked split right: [3, 4, 5]}
}

split_at_mut_checked:与split_at_checked类似,但返回可变的两个切片。如果索引超出范围则返回None

fn main() {let mut v = vec![1, 2, 3, 4, 5];if let Some((left, right)) = v.split_at_mut_checked(2) {for item in left.iter_mut() {*item *= 2;}println!("Modified checked left and right: {:?}, {:?}", left, right);// Modified checked left and right: [2, 4], [3, 4, 5]}
}

split:根据给定的值将Vec分割成多个子切片,返回一个迭代器

fn main() {let v = vec![1, 2, 3, 4, 5];let splits = v.split(|&x| x == 3);for split in splits {println!("Split: {:?}", split);}// Split: [1, 2]// Split: [4, 5]
}

split_mut:与split类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];let splits = v.split_mut(|&x| x == 3);for split in splits {for item in split {*item *= 2;}}println!("Modified vector with split_mut: {:?}", v);// Modified vector with split_mut: [2, 4, 3, 8, 10]
}

split_inclusive:根据给定的值将Vec分割成多个子切片,包括分割值在子切片中,返回一个迭代器

fn main() {let v = vec![1, 2, 3, 4, 5];let splits = v.split_inclusive(|&x| x == 3);for split in splits {println!("Inclusive split: {:?}", split);}// Inclusive split: [1, 2, 3]// Inclusive split: [4, 5]
}

split_inclusive_mut:与split_inclusive类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];let splits = v.split_inclusive_mut(|&x| x == 3);for split in splits {for item in split {*item *= 2;}}println!("Modified vector with split_inclusive_mut: {:?}", v);// Modified vector with split_inclusive_mut: [2, 4, 6, 8, 10]
}

rsplit:从向量的末尾开始根据给定的值将Vec分割成多个子切片,返回一个迭代器

fn main() {let v = vec![1, 2, 3, 4, 5];let splits = v.rsplit(|&x| x == 3);for split in splits {println!("Reverse split: {:?}", split);}// Reverse split: [4, 5]// Reverse split: [1, 2]
}

rsplit_mut:与rsplit类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];let splits = v.rsplit_mut(|&x| x == 3);for split in splits {for item in split {*item *= 2;}}println!("Modified vector with rsplit_mut: {:?}", v);// Modified vector with rsplit_mut: [2, 4, 3, 8, 10]
}

splitn:将Vec分割成指定数量的子切片,返回一个迭代器

fn main() {let v = vec![1, 2, 3, 4, 5];let splits = v.splitn(3, |&x| x % 2 == 0);for split in splits {println!("Splitn: {:?}", split);}// Splitn: [1]// Splitn: [3]// Splitn: [5]
}

splitn_mut:与splitn类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];let splits = v.splitn_mut(3, |&x| x % 2 == 0);for split in splits {for item in split {*item *= 2;}}println!("Modified vector with splitn_mut: {:?}", v);// Modified vector with splitn_mut: [2, 2, 6, 4, 10]
}

rsplitn:从向量的末尾开始将Vec分割成指定数量的子切片,返回一个迭代器

fn main() {let v = vec![1, 2, 3, 4, 5];let splits = v.rsplitn(3, |&x| x % 2 == 0);for split in splits {println!("Reverse splitn: {:?}", split);}// Reverse splitn: [5]// Reverse splitn: [3]// Reverse splitn: [1]
}

rsplitn_mut:与rsplitn类似,但返回可变的子切片迭代器

fn main() {let mut v = vec![1, 2, 3, 4, 5];let splits = v.rsplitn_mut(3, |&x| x % 2 == 0);for split in splits {for item in split {*item *= 2;}}println!("Modified vector with rsplitn_mut: {:?}", v);// Modified vector with rsplitn_mut: [2, 2, 6, 4, 10]
}

contains:判断Vec中是否包含指定元素

fn main() {let v = vec![1, 2, 3];println!("Contains 2? {}", v.contains(&2));// Contains 2? true
}

starts_with:判断Vec是否以指定的切片开头

fn main() {let v = vec![1, 2, 3, 4, 5];let slice = &[1, 2];println!("Starts with slice? {}", v.starts_with(slice));// Starts with slice? true
}

ends_with:判断Vec是否以指定的切片结尾

fn main() {let v = vec![1, 2, 3, 4, 5];let slice = &[4, 5];println!("Ends with slice? {}", v.ends_with(slice));// Ends with slice? true
}

strip_prefix:如果Vec以指定的切片开头,则移除该切片并返回Some包含剩余部分,否则返回None

fn main() {let v = vec![1, 2, 3, 4, 5];if let Some(stripped) = v.strip_prefix(&[1, 2]) {println!("Stripped vector: {:?}", stripped);}// Stripped vector: [3, 4, 5]
}

strip_suffix::如果Vec以指定的切片结尾,则移除该切片并返回Some包含剩余部分,否则返回None

fn main() {let v = vec![1, 2, 3, 4, 5];if let Some(stripped) = v.strip_suffix(&[4, 5]) {println!("Stripped vector: {:?}", stripped);// Stripped vector: [1, 2, 3]}
}

binary_search:在已排序的Vec中进行二分查找,返回一个Result类型,表示查找结果。如果找到元素,返回Ok(index),其中index是元素在向量中的位置;如果未找到,返回Err(index),其中index是元素应该插入的位置以保持向量有序

fn main() {let v = vec![1, 2, 3, 4, 5];let result = v.binary_search(&3);match result {Ok(index) => println!("Found at index {}", index),// Found at index 2Err(index) => println!("Not found, would be inserted at index {}", index),}
}

binary_search_by:使用自定义的比较函数在已排序的Vec中进行二分查找

fn main() {let v = vec![1, 2, 3, 4, 5];let result = v.binary_search_by(|x| x.cmp(&3));match result {Ok(index) => println!("Found at index {}", index),// Found at index 2Err(index) => println!("Not found, would be inserted at index {}", index),}
}

binary_search_by_key:使用自定义的键提取函数和比较函数在已排序的Vec中进行二分查找

fn main() {let v = vec![(1, "a"), (2, "b"), (3, "c")];let result = v.binary_search_by_key(&2, |&(k, _)| k);match result {Ok(index) => println!("Found at index {}", index),// Found at index 1Err(index) => println!("Not found, would be inserted at index {}", index),}
}

sort_unstable:对Vec中的元素进行不稳定排序。不稳定排序可能会改变相等元素的相对顺序

fn main() {let mut v = vec![3, 1, 2];v.sort_unstable();println!("Unstable sorted vector: {:?}", v);// Unstable sorted vector: [1, 2, 3]
}

sort_unstable_by:使用自定义的比较函数对Vec中的元素进行不稳定排序

fn main() {let mut v = vec![(3, "c"), (1, "a"), (2, "b")];v.sort_unstable_by(|a, b| a.0.cmp(&b.0));println!("Unstable sorted by key vector: {:?}", v);// Unstable sorted by key vector: [(1, "a"), (2, "b"), (3, "c")]
}

sort_unstable_by_key:使用自定义的键提取函数对Vec中的元素进行不稳定排序

fn main() {let mut v = vec![(3, "c"), (1, "a"), (2, "b")];v.sort_unstable_by_key(|&(k, _)| k);println!("Unstable sorted by extracted key vector: {:?}", v);// Unstable sorted by extracted key vector: [(1, "a"), (2, "b"), (3, "c")]
}

select_nth_unstable:在Vec中选择第 n 小的元素,并将其移动到正确的位置,同时将小于它的元素移动到左边,大于它的元素移动到右边,但不保证相对顺序


select_nth_unstable_by:使用自定义的比较函数在Vec中选择第 n 小的元素,并进行不稳定排序


select_nth_unstable_by_key:使用自定义的键提取函数在Vec中选择第 n 小的元素,并进行不稳定排序


rotate_left:将Vec向左旋转指定的步数

fn main() {let mut v = vec![1, 2, 3, 4, 5];v.rotate_left(2);println!("Rotated left vector: {:?}", v);// Rotated left vector: [3, 4, 5, 1, 2]
}

rotate_right:将Vec向右旋转指定的步数

fn main() {let mut v = vec![1, 2, 3, 4, 5];v.rotate_right(2);println!("Rotated right vector: {:?}", v);// Rotated right vector: [4, 5, 1, 2, 3]
}

fill:用指定的值填充整个Vec

fn main() {let mut v = vec![1, 2, 3];v.fill(4);println!("Filled vector: {:?}", v);// Filled vector: [4, 4, 4]
}

fill_with:使用一个闭包生成的值填充整个Vec

fn main() {let mut v = vec![1, 2, 3];v.fill_with(|| 5);println!("Filled with closure vector: {:?}", v);// Filled with closure vector: [5, 5, 5]
}

clone_from_slice:从一个切片克隆元素到Vec中,替换Vec的现有内容

fn main() {let mut v = vec![5, 6];let slice = &[7, 8];v.clone_from_slice(slice);println!("Cloned vector: {:?}", v);// Cloned vector: [7, 8]
}

copy_from_slice:从一个切片复制元素到Vec中,从Vec的特定位置开始覆盖,切片长度和Vec长度必须一样

fn main() {let mut v = vec![1, 2];let slice = &[5, 6];v.copy_from_slice(slice);println!("Copied vector: {:?}", v);// Copied vector: [5, 6]
}

copy_within:将元素从切片的一部分复制到自身的另一部分

fn main() {let mut bytes = *b"Hello, World!";bytes.copy_within(1..5, 8);assert_eq!(&bytes, b"Hello, Wello!");
}

swap_with_slice:将切片与另一个切片交换内容

fn main() {let mut slice1 = [0, 0];let mut slice2 = [1, 2, 3, 4];slice1.swap_with_slice(&mut slice2[2..]);println!("slice1:{:?}", slice1);// slice1:[3, 4]println!("slice2:{:?}", slice2);// slice2:[1, 2, 0, 0]
}

partition_point:在已排序的Vec中找到满足特定谓词的分割点。即返回一个索引,使得在该索引之前的元素满足谓词,在该索引之后的元素不满足谓词

fn main() {let v = vec![1, 2, 3, 4, 5];let partition = v.partition_point(|&x| x < 3);println!("Partition point: {}", partition);// Partition point: 2
}

sort:对Vec中的元素进行排序

fn main() {let mut v = vec![3, 1, 2];v.sort();println!("Sorted vector: {:?}", v);// Sorted vector: [1, 2, 3]
}

sort_by:使用自定义的比较函数对Vec中的元素进行稳定排序

fn main() {let mut v = vec![(3, "c"), (1, "a"), (2, "b")];v.sort_by(|a, b| a.0.cmp(&b.0));println!("Sorted by key vector: {:?}", v);// Sorted by key vector: [(1, "a"), (2, "b"), (3, "c")]
}

sort_by_key:使用自定义的键提取函数对Vec中的元素进行稳定排序

fn main() {let mut v = vec![(3, "c"), (1, "a"), (2, "b")];v.sort_by_key(|&(k, _)| k);println!("Sorted by extracted key vector: {:?}", v);// Sorted by extracted key vector: [(1, "a"), (2, "b"), (3, "c")]
}

sort_by_cached_key:使用自定义的键提取函数对Vec中的元素进行稳定排序,并缓存键以提高性能

fn main() {let mut v = vec![(3, "c"), (1, "a"), (2, "b")];v.sort_by_cached_key(|&(k, _)| k);println!("Sorted by cached key vector: {:?}", v);// Sorted by cached key vector: [(1, "a"), (2, "b"), (3, "c")]
}

to_vec:将一个可转换为向量的类型转换为Vec。通常用于将迭代器、切片等转换为向量

fn main() {let slice = &[1, 2, 3];let v = slice.to_vec();println!("Converted to vector: {:?}", v);// Converted to vector: [1, 2, 3]
}

repeat:创建一个包含重复元素的迭代器。可以与其他方法结合使用,如collect来创建一个包含重复元素的Vec

fn main() {let repeated = (0..5).map(|_| 1).collect::<Vec<_>>();println!("Repeated vector: {:?}", repeated);// Repeated vector: [1, 1, 1, 1, 1]
}

concat:连接多个切片或展平为单个值

fn main() {let s = ["hello", "world"].concat();println!("{}", s);// helloworld
}

join:将向量中的元素用指定的分隔符连接成一个字符串

fn main() {let v = vec!["a", "b", "c"];let joined = v.join(",");println!("Joined string: {}", joined);// Joined string: a,b,c
}

to_ascii_uppercase:将Vec中的所有 ASCII 字符转换为大写。用于将向量中的 ASCII 字符转换为大写形式
to_ascii_lowercase:将Vec中的所有 ASCII 字符转换为小写。前面也有提及,用于将向量中的 ASCII 字符转换为小写形式

fn main() {let original = vec![97, 98, 65, 66, 200];let uppercase_result = original.to_ascii_uppercase();println!("Uppercase result: {:?}", uppercase_result);// Uppercase result: [65, 66, 65, 66, 200]let lowercase_result = original.to_ascii_lowercase();println!("Lowercase result: {:?}", lowercase_result);// Lowercase result: [97, 98, 97, 98, 200]
}

相关文章:

详解Rust标准库:Vec向量

查看本地官方文档 安装rust后运行 rustup doc查看The Standard Library即可获取标准库内容 std::vec::Vec定义 Vec除了可以作为动态数组还可以模拟为一个栈&#xff0c;仅使用push、pop即可 Vec默认分配在堆上&#xff0c;对于一个容量为4&#xff0c;有两个元素a、b的向量…...

网络原理(初一,TCP/IP五层(或四层)模型面试问题)

TCP/IP五层&#xff08;或四层&#xff09;模型 TCP/IP是⼀组协议的代名词&#xff0c;它还包括许多协议&#xff0c;组成了TCP/IP协议簇。 TCP/IP通讯协议采⽤了5层的层级结构&#xff0c;每⼀层都呼叫它的下⼀层所提供的⽹络来完成⾃⼰的需求。 • 应⽤层&#xff1a;负责…...

Unity引擎材质球残留贴图引用的处理

大家好&#xff0c;我是阿赵。   这次来分享一下Unity引擎材质球残留贴图引用的处理 一、 问题 在使用Unity调整美术效果的时候&#xff0c;我们很经常会有这样的操作&#xff0c;比如&#xff1a; 1、 同一个材质球切换不同的Shader、 比如我现在有2个Shader&#xff0c;…...

Flutter鸿蒙next中封装一个列表组件

1. 创建Flutter项目 首先&#xff0c;确保你已经安装了Flutter SDK&#xff0c;并创建一个新的Flutter项目&#xff1a; flutter create podcast_app cd podcast_app2. 封装列表组件 我们将在lib目录下创建一个新的文件&#xff0c;命名为podcast_list.dart&#xff0c;用于…...

层次与网络的视觉对话:树图与力引导布局的双剑合璧

目录 目的内容树图绘制目的步骤参考代码结果与分析 力引导布局算法目的参考代码结果与分析 总结 目的 掌握常用可视化软件与工具&#xff1a;学习和熟练使用常用的数据可视化软件和工具&#xff0c;如Matplotlib、Seaborn、Plotly、Tableau等。这些工具提供了用于创建图表、图…...

python将数据集中所有文件名升序制作txt文件(医学影像)

import os import re # 设定图像文件所在的路径 img_path ./2d/images/ #需修改路径 # 获取该路径下的所有文件名 img_list os.listdir(img_path) # 过滤出以.nii结尾的文件名 nii_list [f for f in img_list if f.endswith(.nii)] # 使用正则表达式从文件名中提…...

【The Art of Unit Testing 3_自学笔记06】3.4 + 3.5 单元测试核心技能之:函数式注入与模块化注入的解决方案简介

文章目录 3.4 函数式依赖注入技术 Functional injection techniques3.5 模块化依赖注入技术 Modular injection techniques 写在前面 上一篇的最后部分对第三章后续内容做了一个概括性的梳理&#xff0c;并给出了断开依赖项的最简单的实现方案&#xff0c;函数参数值注入法。本…...

【VSCode】配置

安装插件 C vscode-icons gdb调试 https://www.bilibili.com/video/BV15U4y1x7b2/?spm_id_from333.999.0.0&vd_sourcedf0ce73d9b9b61e6d4771898f1441f7f https://www.bilibili.com/video/BV1pU4y1W74Z?spm_id_from333.788.recommend_more_video.-1&vd_sourcedf0…...

Linux 常用命令整理大全及命令使用心得

本文章是为了总结自己用过的命令&#xff0c;以及一些心得&#xff0c;网上有很多类似的&#xff0c;但自己总结才能更好的理解。 文章目录 一、文件和目录管理01、 ls &#xff1a;列出目录内容02、cd&#xff1a;更改当前目录03、pwd&#xff1a;显示当前工作目录04、mkdir&a…...

计算器的实现

计算器的⼀般实现 计算器的一般实现&#xff1a;优化&#xff1a;使⽤函数指针数组的实现&#xff1a; 计算器的一般实现&#xff1a; #include <stdio.h> int add(int a, int b) {return a b; } int sub(int a, int b) {return a - b; } int mul(int a, int b) {retur…...

这个工具帮你快速实现数据集成和同步

在这个信息爆炸的时代&#xff0c;数据的流动和同步逐渐成为企业运营的命脉。然而&#xff0c;企业正面临着前所未有的数据挑战&#xff0c;无论是跨地域的分公司协作&#xff0c;还是云服务与本地数据库的交互&#xff0c;数据的集成、清洗、转换和加载&#xff08;ETL&#x…...

论文阅读:Computational Long Exposure Mobile Photography (一)

这篇文章是谷歌发表在 2023 ACM transaction on Graphic 上的一篇文章&#xff0c;介绍如何在手机摄影中实现长曝光的一些拍摄效果。 Abstract 长曝光摄影能拍出令人惊叹的影像&#xff0c;用运动模糊来呈现场景中的移动元素。它通常有两种模式&#xff0c;分别产生前景模糊或…...

项目解决方案:多地连锁药店高清视频监控系统建设解决方案(设计方案)

​ 目录 一.项目背景 1.1背景描述 1.2需求分析 二.设计依据和建设目标 2.1设计依据 2.2建设目标 三.系统设计实现 3.1系统方案设计 3.2网络组网说明 四.建设系统特色 4.1安全性 4.2节约建设成本 4.3原有资源的再利用 4.4可扩展性 五.产品介绍 5.1概述 5.2设备…...

utf-8、pbkdf2_sha

#utf-8加密、解密 import base64 base64.b64encode(lienlien123.encode(utf-8)) bbGllbmxpZW4xMjM base64.b64decode(bbGllbmxpZW4xMjM.decode(utf-8)) blienlien123 #pbkdf2_sha加密&#xff0c;校验 # 该种密码在不同时刻会有产生不同的加密结果 # 该加密方法使用的是散列…...

Java之包,抽象类,接口

目录 包 导入包 静态导入 将类放入包 常见的系统包 抽象类 语法规则 注意事项&#xff1a; 抽象类的作用 接口 实现多个接口 接口间的继承 接口使用实例 &#xff08;法一&#xff09;实现Comparable接口的compareTo()方法 &#xff08;法二&#xff09;实现Comp…...

HarmonyOS鸿蒙开发入门,常用ArkUI组件学习(二)

书接上回&#xff0c;让我们继续来学习ArkUI的其他组件 目录&#xff0c;可以点击跳转到想要了解的组件详细内容 组件四&#xff1a;Button组件五&#xff1a;Slider组件六&#xff1a; Column & Row组件七&#xff1a;循环控制组件八&#xff1a; List 组件四&#xff1a;…...

斩!JavaScript语法进阶

一、DOM 概述 DOM 是 JavaScript 操作网页的接口&#xff0c;全称为“文档对象模型”&#xff08;Document Object Model&#xff09;。当网页被加载时&#xff0c;浏览器将网页转为一个DOM&#xff0c;并用JS进行各种操作。比如&#xff1a;改变页面中的HTML 元素及其属性&am…...

UFO:Windows操作系统的具象智能代理

近年来&#xff0c;随着AI技术的发展&#xff0c;智能代理在各种应用中扮演着越来越重要的角色。微软推出的UFO&#xff08;User-Focused Operator&#xff09;正是这样一个出色的多代理框架&#xff0c;旨在通过无缝导航和操作&#xff0c;满足用户在Windows操作系统中跨多个应…...

win10/11无休眠设置和断电后电池模式自动休眠而不是睡眠-用以省电

1、打开休眠设置选项 打开控制面板\所有控制面板项\电源选项\ 左侧的选择电源按钮的功能 默认状态没有休眠 1、管理员权限打开cmd或者power shell 2、输入一下指令&#xff0c;打开休眠选项 powercfg -hibernate on关闭后重新打开 控制面板\所有控制面板项\电源选项\左侧的选…...

【动态规划之斐波那契数列模型】——累加递推型动态规划

文章目录 第N个泰波那契数列面试题08.01.三步问题使用最小花费爬楼梯解码问题 第N个泰波那契数列 解题思路&#xff1a; 泰波那契数列的第 N 项定义为前面三项之和&#xff0c;即 T0 0, T1 1, T2 1&#xff0c;从 T3 开始&#xff0c;每一项都等于前三项的和。要找到第 N 项…...

5g通信系统用到的crc码

5g通信系统用到的crc码 关注 在5G通信系统中&#xff0c;CRC码&#xff08;循环冗余校验码&#xff09;扮演着关键角色&#xff0c;它通过执行多项式除法运算来检测数据在传输过程中是否发生错误。5G通信系统中采用了多种CRC码&#xff0c;每种码都有其独特的计算方法和校验特…...

Ubuntu-22.04 虚拟机安装

1. Ubuntu安装方式 1.1. 基于物理介质安装 光盘安装&#xff1a;通过将 Ubuntu 镜像刻录到光盘&#xff0c;在计算机 BIOS/UEFI 中设置光盘为第一启动项&#xff0c;然后按照安装程序的提示进行语言选择、分区、用户信息设置等操作来完成安装。这种方式需要有光盘刻录设备和空…...

Windows、Linux系统上进行CPU和内存压力测试

CPU和内存压力测试 1. Linux环境 Linux环境下&#xff0c;我们可以用 stress 工具进行内存、CPU等的压力测试。 【1】. stress工具说明 [kalamikysrv1 ~]$ stress --help stress imposes certain types of compute stress on your systemUsage: stress [OPTION [ARG]] ...-…...

FFmpeg 4.3 音视频-多路H265监控录放C++开发八,使用SDLVSQT显示yuv文件 ,使用ffmpeg的AVFrame

一. AVFrame 核心回顾&#xff0c;uint8_t *data[AV_NUM_DATA_POINTERS] 和 int linesize[AV_NUM_DATA_POINTERS] AVFrame 存储的是解码后的数据&#xff0c;&#xff08;包括音频和视频&#xff09;例如&#xff1a;yuv数据&#xff0c;或者pcm数据&#xff0c;参考AVFrame结…...

HTML 标签属性——<a>、<img>、<form>、<input>、<table> 标签属性详解

文章目录 1. `<a>`元素属性hreftargetname2. `<img>`元素属性srcaltwidth 和 height3. `<form>`元素属性actionmethodenctype4. `<input>`元素属性typevaluenamereadonly5. `<table>`元素属性cellpaddingcellspacing小结HTML元素除了可以使用全局…...

css简写属性

一些属性&#xff0c;如 font、background、padding、border 和 margin 等属性称为简写属性。它们允许在一行中设置多个属性值&#xff0c;从而节省时间并使代码更整洁。 /* 在像 padding 和 margin 这样的 4 值简写语法中&#xff0c;数值的应用顺序是上、右、下、左&#xff…...

力扣刷题(sql)--零散知识点(2)

1.自定义分组后的分类统计问题&#xff08;某组内无数据却仍要展示&#xff09; 例题1&#xff1a; 查询每个工资类别的银行账户数量。 工资类别如下&#xff1a; "Low Salary"&#xff1a;所有工资 严格低于 20000 美元。"Average Salary"&#xff1a;…...

TCP是怎样工作的网络拥塞控制理论和算法部分记录

参考资料 https://github.com/ituring/tcp-book 流量控制、窗口控制和拥塞控制的关系 流量控制、窗口控制和拥塞控制的关系如图所示 窗口控制是上层的概念&#xff0c;核心思路是基于滑动窗口技术传输数据。而确定发送窗口大小的方法有流量控制和拥塞控制两种 流量控制&…...

CSRF初级靶场

靶场 针对DVWA么有防御 源码&#xff1a; <?phpif( isset( $_GET[ Change ] ) ) {// Get input$pass_new $_GET[ password_new ];$pass_conf $_GET[ password_conf ];// Do the passwords match?if( $pass_new $pass_conf ) {// They do!$pass_new ((isset($GLOBA…...

CSP/信奥赛C++刷题训练:经典差分例题(2):洛谷P9904 :Mieszanie kolorów

CSP/信奥赛C++刷题训练:经典差分例题(2):洛谷P9094 :Mieszanie kolorw 题目描述 题目译自 PA 2020 Runda 1 Mieszanie kolorw Byteasar 正准备给栅栏涂漆。他已经准备了 n n n 罐白色油漆,他把这些油漆排列成一排,从 1 1 1 到 n n n 编号。他想用这些油漆,但他不想…...