20天学会rust(四)常见系统库的使用

前面已经学习了rust的基础知识,今天我们来学习rust强大的系统库,从此coding事半功倍。

集合

数组&可变长数组

在 Rust 中,有两种主要的数组类型:固定长度数组(Fixed-size Arrays)和可变长度数组(Dynamic-size Arrays)。

  1. 固定长度数组(Fixed-size Arrays):
    固定长度数组的长度在编译时就确定,并且长度不可改变。你可以使用以下语法定义一个固定长度数组:
fn main() {let arr: [i32; 5] = [1, 2, 3, 4, 5];
}

在上面的例子中,我们定义了一个类型为 i32 的固定长度数组 arr ,长度为 5,并初始化了数组的元素。

  1. 可变长度数组(Dynamic-size Arrays):
    Rust 中没有直接支持可变长度数组的语法。但是,你可以使用 Vec<T> 类型来创建一个动态增长的数组。 Vec<T> 是一个可变长度的动态数组,可以根据需要动态添加和删除元素。以下是一个使用 Vec<T> 的示例:
fn main() {let mut vec: Vec<i32> = Vec::new();vec.push(1);vec.push(2);vec.push(3);
}

在上面的例子中,我们首先创建了一个空的 Vec<i32> ,然后使用 push 方法向数组中添加元素。 Vec<T> 会根据需要自动调整大小。

需要注意的是,可变长度数组( Vec<T> )和固定长度数组( [T; N] )是不同的类型,它们具有不同的性质和用途。下面我们再看下Vec的一些基础用法:

创建Vec
在 Rust 中,有几种创建 Vec 的方式:

  1. 使用 Vec::new() 创建一个空的 Vec:
let mut vec = Vec::new();

这会创建一个长度为 0 的空向量。

  1. 使用 vec![] 宏创建一个非空的 Vec:
let mut vec = vec![1, 2, 3];

这会创建一个长度为 3、值为 [1, 2, 3] 的可变数组。

  1. 使用 Vec::with_capacity() 创建一个指定容量的 Vec:
let mut vec = Vec::with_capacity(10);

这会创建一个长度为 0 但预分配了 10 个元素空间的向量。这意味着,在不重新分配内存的情况下,vec 可以增长到 10 个元素。

  1. 使用 iterator 方法创建一个 Vec:
let mut vec = (1..10).collect::<Vec<i32>>();

这会创建一个长度为 9、值为 [1, 2, 3, 4, 5, 6, 7, 8, 9] 的向量。

这些方法的主要区别在于:

  • Vec::new() 和 vec![] 创建的向量初始长度为 0 或指定长度。
  • Vec::with_capacity() 创建的向量初始长度为 0,但预分配了指定的容量。这可以提高向量在后续增长时的性能,因为不需要频繁重新分配内存。
  • 使用 iterator 方法创建的向量会根据 iterator 的长度创建指定大小的向量。

总的来说,如果你知道向量的大致大小,使用 Vec::with_capacity() 可以获得最好的性能。否则,Vec::new() 和 vec![] 也是不错的选择。这里的区别类似Java。

操作Vec
在 Rust 中, Vec<T> 是一个可变长度的动态数组,用于存储相同类型的多个值。它有以下主要的方法:

  1. 添加元素 - 使用 push() 方法:
let mut vec = Vec::new();
vec.push(1);
  1. 删除元素 - 使用 pop() 方法删除最后一个元素:
let mut vec = vec![1, 2, 3];
vec.pop(); // vec = [1, 2]
  1. 查找元素 - 使用索引( [] )访问元素:
let vec = vec![1, 2, 3];
let elem = vec[0]; // elem = 1
  1. 修改元素 - 也使用索引( [] )访问元素,然后对其进行修改:
let mut vec = vec![1, 2, 3];
vec[0] = 5; 
// vec = [5, 2, 3]

除此之外, Vec<T> 还有其他方法,比如:

  • len() - 获取向量长度
  • iter() - 创建一个迭代器以便于遍历元素
  • iter_mut() - 创建一个可变迭代器以便于修改元素
  • first() - 获取第一个元素
  • last() - 获取最后一个元素
  • get() - 安全地访问一个元素,返回 Option<&T>
  • get_mut() - 安全地访问一个可变元素,返回 Option<&mut T>

这些方法可以满足你对 Vec<T> 的基本操作需求。

map

在 Rust 中,有几种主要的 map 实现:

  1. HashMap - 散列表映射,基于哈希表实现。这是 Rust 中最常用的映射类型。
  2. BTreeMap - 二叉树映射,基于二叉树实现。键值是有序的。
  3. LinkedHashMap - 链表散列表映射,基于哈希表和双向链表实现。键值保持插入顺序。
  4. IndexMap - 基于数组的映射。键必须实现 Index trait。
  5. FxHashMap - 散列表映射,使用第三方 fxhash 算法实现。性能可能优于标准库的 HashMap

这里主要介绍HashMap的用法,其他的大同小异,后续会出一个专门的用法介绍。
在 Rust 中,HashMap 的主要用法如下:

  1. 创建一个空的 HashMap:
let mut map = HashMap::new();
  1. 插入键值对:
map.insert(1, "a");
  1. 获取值:
let a = map.get(&1); // a = Some("a")
  1. 删除键值对:
map.remove(&1);
  1. 迭代 HashMap:
for (key, value) in &map {println!("{}: {}", key, value);
}
  1. 检查键是否存在:
map.contains_key(&1); // true 或 false
  1. 获取 HashMap 的长度:
let len = map.len();
  1. 清空 HashMap:
map.clear();
  1. 只迭代键或值:
for key in map.keys() {println!("{}", key); 
}for value in map.values() {println!("{}", value);
}

这些是 HashMap 在 Rust 中最基本和最常用的方法。HashMap 是一个无序的 map,键类型必须实现 EqHash trait。

Set

  1. HashSet - 散列表集合,基于哈希表实现。用于存储唯一的键。
  2. BTreeSet - 二叉树集合,基于二叉树实现。键值是有序的。

这里我们也重点介绍HashSet的用法:

  1. HashSet基础用法:
let mut set = HashSet::new();
set.insert(1); // 插入元素:
set.remove(&1);// 删除元素:
set.contains(&1); // 检查元素是否存在: true 或 false
let len = set.len();// 获取 HashSet 的长度:for elem in &set { // 迭代 HashSet:println!("{}", elem);
}set.clear(); // 清空 HashSet:
  1. 取两个 HashSet 的交集、并集、差集:
let set1 = HashSet::from([1, 2, 3]);
let set2 = HashSet::from([2, 3, 4]);let intersection = set1.intersection(&set2).collect(); // [2, 3]
let union = set1.union(&set2).collect(); // [1, 2, 3, 4]
let difference = set1.difference(&set2).collect(); // [1]

HashSet 是一个无序的集合,元素类型必须实现 EqHash trait。

迭代器&流式编程(Iterator)

在 Rust 中,迭代器(Iterator)是一种用于遍历集合元素的抽象。它提供了一个统一的接口,使你可以对各种不同类型的集合进行迭代。

要使用迭代器,你可以按照以下步骤进行操作:

  1. 创建一个迭代器:
    你可以通过调用集合上的 .iter().iter_mut() 方法来创建一个不可变或可变的迭代器。例如:
let vec = vec![1, 2, 3];
let iter = vec.iter(); // 不可变迭代器
let mut_iter = vec.iter_mut(); // 可变迭代器
  1. 使用迭代器方法:
    一旦你创建了迭代器,你可以使用迭代器的方法来处理集合的元素。一些常见的方法包括 .next().map().filter().fold() 等。例如:
let vec = vec![1, 2, 3];
let mut iter = vec.iter();// 使用 .next() 方法逐个获取元素
while let Some(item) = iter.next() {println!("{}", item);
}// 使用 .map() 方法对元素进行转换
let vec2: Vec<i32> = vec.iter().map(|x| x * 2).collect();
println!("{:?}", vec2); // 输出 [2, 4, 6]// 使用 .filter() 方法过滤元素
let vec3: Vec<i32> = vec.iter().filter(|x| *x > 1).cloned().collect();
println!("{:?}", vec3); // 输出 [2, 3]
  1. 链式调用迭代器方法:
    你可以使用链式调用来组合多个迭代器方法,以实现复杂的操作。例如:
let vec = vec![1, 2, 3];
let result: i32 = vec.iter().filter(|x| *x > 1).map(|x| x * 2).sum();
println!("{}", result); // 输出 10

通过这些方法,你可以对集合进行各种操作,如过滤、映射、折叠等。

  1. 自定义迭代器

在 Rust 中,你可以通过实现 Iterator trait 来自定义迭代器。 Iterator trait 有以下定义:

trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;// 默认方法fn size_hint(&self) -> (usize, Option<usize>) { ... }fn count(self) -> usize { ... }fn last(self) -> Option<Self::Item> { ... }fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }fn step_by(self, step: usize) -> StepBy<Self> { ... }// 等等
}

要实现这个 trait,你需要:

  1. 定义 Item 类型,表示迭代器返回的元素类型。

  2. 实现 next 方法,返回迭代器的下一个元素,如果迭代器结束则返回 None

  3. 可选:实现其他默认方法来改善迭代器的行为。

以下是一个自定义迭代器的示例:

struct Counter {count: u32,
}impl Iterator for Counter {type Item = u32;fn next(&mut self) -> Option<Self::Item> {if self.count < 5 {let c = self.count;self.count += 1;Some(c)} else {None}}
}fn main() {let mut iter = Counter { count: 0 };while let Some(i) = iter.next() {println!("{}", i);}
}

这个迭代器会返回 0 到 4 的数字,然后结束。我们实现了 next 方法来定义这个行为。

通过实现 Iterator trait,你可以创建各种自定义的迭代器,以满足不同的需求。希望这能帮助你理解 Rust 中的迭代器和如何自定义迭代器!如果还有其他问题,请随时提问。

并发

多线程处理

下面我们通过一个例子学习: 创建一个线程,并每隔2s输出“hello world”

上代码:

use std::thread;
use std::time::Duration;fn main() {thread::spawn(|| {loop {println!("hello world");thread::sleep(Duration::from_secs(2));}});
}

这个代码会:

  • 使用 thread::spawn 创建一个新线程
  • 在线程中有一个无限循环
  • 每次循环会打印 “hello world”
  • 使用 thread::sleep 使线程睡眠 2 秒
  • 所以这个线程会每隔 2 秒打印一次 “hello world”

Duration::from_secs(2) 是创建一个表示 2 秒的 Duration。我们将其传递给 thread::sleep 来使线程睡眠 2 秒。

Ok,我们掌握了线程的基础用法, 再来看一个复杂的例子: 用多线程实现观察者模式

要实现观察者模式,可以使用通道(channel)在线程间通信。例如:

use std::sync::mpsc;
use std::thread;struct Subject {observers: Vec<mpsc::Sender<i32>>,
}impl Subject {fn new() -> Subject {Subject { observers: vec![] }}fn attach(&mut self, observer: mpsc::Sender<i32>) {self.observers.push(observer);}fn notify(&self) {for observer in self.observers.iter() {observer.send(1).unwrap();}}
}fn main() {let (tx1, rx1) = mpsc::channel();let (tx2, rx2) = mpsc::channel();let mut subject = Subject::new();subject.attach(tx1);subject.attach(tx2);thread::spawn(move || {let msg = rx1.recv().unwrap();println!("Got: {}", msg);});thread::spawn(move || {let msg = rx2.recv().unwrap();println!("Got: {}", msg);});subject.notify();
}

在这个例子中:

  • Subject 结构体充当主题(subject),维护多个观察者(observers)的列表。
  • attach 方法用于添加观察者(通道的发送端)。
  • notify 方法用于通知所有观察者(通过通道发送消息)。
  • 我们创建两个线程作为观察者,通过通道接收主题的通知。
  • 当调用 subject.notify() 时,两个观察者线程会接收到通知并打印消息。

线程间通信

在上面的例子中,有一个新的知识点:使用通道(channel)在线程间通信。 那么什么是channel呢?

在 Rust 中,channel 用于在线程之间发送消息和通信。它可以在不同的线程之间安全地传递数据。

channel 的主要作用有:

  1. 线程间通信:channel 可以在不同的线程之间发送消息,用于线程间的通信和协作。

  2. 安全地共享数据:channel 可以在线程之间安全地传递数据,避免数据竞争。

  3. 限制并发:channel 的发送端和接收端各有一个缓存,这可以限制线程之间并发发送和接收消息的数量。

举个例子:

use std::sync::mpsc::channel;
use std::thread;fn main() {let (tx, rx) = channel();thread::spawn(move || {tx.send(10).unwrap();});let received = rx.recv().unwrap();println!("Got: {}", received);
}

在这个例子中:

  • 我们使用 channel() 创建一个 channel,它返回一个发送端 tx 和一个接收端 rx
  • 然后我们创建一个线程,在线程中通过 tx 发送消息 10。
  • 在主线程中,我们通过 rx 接收该消息,并打印结果。
  • 这样,通过 channel 我们就在两个线程之间安全地传递了数据。

channel 在 Rust 的并发编程中非常有用,它可以用于线程池、工作窃取等并发模式中。
再看一个用法: 工作窃取

这里是一个使用 channel 实现工作窃取的例子:

use std::sync::mpsc::channel;
use std::thread;fn main() {let (tx, rx) = channel();let mut threads = vec![];for i in 0..10 {let tx = tx.clone();threads.push(thread::spawn(move || {let mut work = rx.recv().unwrap();while let Some(job) = work.recv() {println!("Worker {} got job {}", i, job);}}));}for job in 0..10 {let thread_id = job % threads.len();threads[thread_id].send(job).unwrap();}
}

这个例子做了以下工作:

  1. 创建一个 channel,得到发送端 tx 和接收端 rx。

  2. 创建 10 个工作线程,每个线程从 rx 接收工作。

  3. 将 10 个工作(job)发送到不同的工作线程,实现工作窃取。每个工作会被发送到线程 ID 与工作 ID 取余后的线程。

  4. 每个工作线程接收工作,并打印接收到的工作 ID。

  5. 主线程将所有工作分发完成后结束。

这个例子展示了如何使用 channel 在线程之间传递工作,实现工作窃取的模式。每个工作线程从 channel 接收工作,而不是固定的工作队列。这使得工作可以在线程之间动态分配,实现工作窃取。

线程池

熟悉java的同学可能会问了,有线程? 那有线程池吗?必须有!!

在 Rust 中,你可以使用 threadpool crate 来创建一个线程池。以下是一个基本的示例:

use threadpool::ThreadPool;fn main() {let pool = ThreadPool::new(4);for i in 0..10 {let j = i;pool.execute(move || {println!("Hello from thread {}", j);});}
}

这会创建一个包含 4 个线程的线程池。然后我们使用 pool.execute() 方法在线程池中执行 10 个闭包。这些闭包会被线程池的线程执行,并打印出执行线程的索引。

threadpool crate 提供了以下主要功能:

  • ThreadPool::new(size) :创建一个包含 size 个线程的线程池。
  • pool.execute(closure) :在线程池的某个线程中执行提供的闭包。
  • pool.join() :等待线程池中的所有线程结束。
  • ThreadPoolBuilder :可以用来自定义线程池的各种设置,如线程名称、堆栈大小等。

一个更复杂的例子:

use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use threadpool::ThreadPool;fn main() {let pool = ThreadPool::new(4);let (tx, rx) = mpsc::channel();let tx = Arc::new(Mutex::new(tx));for i in 0..10 {let tx = tx.clone();pool.execute(move || {let mut tx = tx.lock().unwrap();tx.send(i).unwrap();});}for _ in 0..10 {let j = rx.recv().unwrap();println!("Got: {}", j);}pool.join();
}

这里我们使用了 mpsc 库创建一个通道,并使用 Arc<Mutex<T>> 在线程之间共享该通道的发送端。然后我们在 10 个任务中发送数字到通道,并在主线程中接收这些数字。

序列化

在后端开发中,序列化是一个绕不过的话题,前后端交互,后端之间交互都需要对数据做序列化与反序列化。 在 Rust 中,有几种常用的序列化方式:

  1. Serde:这是 Rust 中最流行的序列化库。它提供了多种序列化格式的支持,如 JSON、YAML、TOML 等。使用 Serde 可以方便地将 Rust 结构体序列化为这些格式,以及反序列化回 Rust 结构体。

  2. Bincode:这是一个用于在 Rust 中进行二进制序列化的库。它可以高效地将 Rust 的基本数据结构编码为二进制,并解码回原数据结构。

  3. Ron:这是一个用于 Rust 对象表示法 (RON) 的序列化格式和解析器。RON 是一个人类友好的二进制序列化格式,设计用于在 Rust 中存储和传输数据。

  4. CBOR:这是一个实现了约束二进制对象表示法 (CBOR) 的 Rust 库。CBOR 是一种二进制序列化格式,它比 JSON 更紧凑,也更适用于嵌入式系统。

  5. MessagePack:这是一个实现 MessagePack 二进制序列化格式的 Rust 库。MessagePack 是一个高效的二进制序列化格式,可以用于在不同语言之间交换数据。

  6. Protobuf:这是 Google 开发的一种数据序列化格式,在 Rust 中可以使用 prostprotobuf 库来实现。Protobuf 是一种语言无关、平台无关的可扩展机制,用于序列化结构化数据。

以上就是 Rust 中常用的几种序列化方式。总的来说,如果你需要一个通用的序列化方式,可以选择 Serde。如果你需要一个高效紧凑的二进制格式,可以选择 Bincode、CBOR 或 MessagePack。如果你需要跨语言支持,可以选择 Protobuf。

这里主要演示下Serde的用法,它提供了一组宏和 trait,用于将 Rust 数据结构转换为各种格式的序列化表示,并将序列化表示转换回 Rust 数据结构。

要使用 Serde,首先需要在 Cargo.toml 文件中添加以下依赖项:

[dependencies]
serde = "1.0"
serde_json = "1.0"

接下来,我们将给出一个使用 Serde 进行 JSON 序列化和反序列化的例子:

use serde::{Serialize, Deserialize};
use serde_json::{Result, Value};#[derive(Serialize, Deserialize, Debug)]
struct Person {name: String,age: u8,address: String,
}fn main() -> Result<()> {// 序列化为 JSONlet person = Person {name: "Alice".to_string(),age: 25,address: "123 ABC Street".to_string(),};let serialized = serde_json::to_string(&person)?;println!("Serialized: {}", serialized);// 反序列化为 Rust 结构体let deserialized: Person = serde_json::from_str(&serialized)?;println!("Deserialized: {:?}", deserialized);Ok(())
}

在上面的例子中,我们定义了一个 Person 结构体,并使用 #[derive(Serialize, Deserialize)] 宏为其实现了 Serde 的 SerializeDeserialize trait。这使得我们可以将 Person 结构体序列化为 JSON 字符串,并将 JSON 字符串反序列化为 Person 结构体。

main 函数中,我们首先创建一个 Person 实例,然后使用 serde_json::to_string 方法将其序列化为 JSON 字符串,并打印出来。接着,我们使用 serde_json::from_str 方法将 JSON 字符串反序列化为 Person 结构体,并打印出来。

网络请求

请求Http接口

在 Rust 中,你可以使用第三方库来进行 HTTP 请求。最常用的库之一是 reqwest ,它提供了简单且易于使用的 API 来发送 HTTP 请求。

首先,你需要在 Cargo.toml 文件中添加 reqwest 依赖项:

[dependencies]
reqwest = "0.11"

接下来,我们将给出一个使用 reqwest 发送 GET 请求的示例:

use reqwest::Error;#[tokio::main]
async fn main() -> Result<(), Error> {let response = reqwest::get("https://api.example.com/users").await?;let body = response.text().await?;println!("Response Body: {}", body);Ok(())
}

在上面的示例中,我们使用 reqwest::get 方法发送一个 GET 请求到 https://api.example.com/users 。然后,我们使用 response.text().await? 来获取响应的文本内容,并打印出来。

需要注意的是,我们使用了 tokio::main 宏来异步运行请求。因此,你需要在 main 函数之前添加 tokio 作为依赖项,并在 main 函数前面使用 #[tokio::main] 注解。

到目前为止,我们学习了集合、并发、序列化和网络请求,最后再留一个作业: 请求百度首页,并解析出所有的http链接。 结合本文所学哦

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

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

相关文章

Ajax如何理解

什么是ajax ●认识前后端交互 ○就是 前端 与 后端的 一种通讯方式 ○主要使用的技术栈就是 ajax (async javascript and xml) ●ajax 特点 ○使用 ajax 技术网页应用能够快速的将新内容呈现在用户界面 ○并且不需要刷新整个页面, 也就是能够让页面有 "无…

Java技术整理(5)—— Spring篇

Spring是一个全面的全面的、企业应用开发一站式的解决方案&#xff0c;贯穿表现层、业务层、持久层。但是 Spring 仍然可以和其他的框架无缝整合。 1、Spring的核心组件 &#xff08;1&#xff09;数据层&#xff1a; JDBC、ORM、OXM、JMS、Transations &#xff08;2&#x…

Flutter 中,ListView 中需要放置 ListView 需要怎么处理才高效?

问题及场景 ListView 是 Flutter 开发者第一个学习到的 Widget&#xff0c;因为它可以滑动。一切都会运行得很好&#xff0c;直到 ListView 中的 Item 本身也是一个 ListView。你可能会看到 Flutter 建议你将内部的 ListView 的ShrinkWrap 属性设置为 True。虽然错误消除了&am…

连续两年增收不增利,比亚迪电子靠新能源汽车业务再次起飞?

在净利润连续两年下挫之后&#xff0c;比亚迪电子&#xff08;00285.HK&#xff09;终于迎来了好消息。 不久前比亚迪电子发布2023年中期盈利预告显示&#xff0c;上半年净利润同比增加115%-146%&#xff08;2022年上半年的净利润显示6.34亿元&#xff09;。 这主要受益于大客…

包管理工具 nvm npm nrm yarn cnpm npx pnpm详解

包管理工具 nvm npm yarn cnpm npx pnpm npm、cnpm、yarn、pnpm、npx、nvm的区别&#xff1a;https://blog.csdn.net/weixin_53791978/article/details/122533843 npm、cnpm、yarn、pnpm、npx、nvm的区别&#xff1a;https://blog.csdn.net/weixin_53791978/article/details/1…

【Freertos基础入门】2个Freertos的Delay函数

文章目录 前言一、vTaskDelay与vTaskDelayUntil二、示例代码总结 前言 本系列基于stm32系列单片机来使用freerots 任务管理是实时操作系统&#xff08;RTOS&#xff09;的核心功能之一&#xff0c;它允许开发者以并发的方式组织和管理多个任务。FreeRTOS 是一个流行的开源RTO…

嵌入式开发中常用且杂散的命令

1、mount命令 # 挂载linux系统 mkdir /tmp/share mount -t nfs 10.77.66.88:/share/ /tmp/share -o nolock,tcp cd /tmp/share# 挂载Windows系统 mkdir /tmp/windows mount -t nfs 10.66.77.88:/c/public /tmp/windows -o nolock,tcp cd /tmp/windows# 挂载vfat格式的U盘 mkdi…

强训第32

选择 D B A A 发送TCP意思应该是已经建立了连接&#xff0c;会超时重传。在未建立连接的时候&#xff0c;会放弃该链接 C A 80端口是http A 交换机攻击主要有五种&#xff1a;VLAN跳跃攻击 生成树攻击 MAC表洪水攻击 ARP攻击 VTP攻击 B A 2^(32-26)2^(32-27)2^(32-27)128 减去…

基于Java+SpringBoot+Vue+echarts健身房管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

maven Jar包反向install到本地仓库

maven Jar包反向install到本地仓库 需求实现 需求 项目打包时报错&#xff0c;缺少一个jar包。 但是在maven仓库都找不到此jar包&#xff0c;其他人提供了这个jar包。 需要把这个jar包install到本地仓库&#xff0c;使项目能正常打包运行。 实现 使用git bash命令执行以下脚…

16.3.4 【Linux】系统资源的观察

free &#xff1a;观察内存使用情况 系统当中有 2848MB 左右的实体内存&#xff0c;我的 swap 有 1GB 左右&#xff0c; 那我使用free -m 以 MBytes 来显示时&#xff0c;就会出现上面的信息。Mem 那一行显示的是实体内存的量&#xff0c;Swap 则是内存交换空间的量。 total 是…

C++多态

文章目录 &#x1f435;1. 什么是多态&#x1f436;2. 构成多态的条件&#x1f429;2.1 虚函数&#x1f429;2.2 虚函数的重写&#x1f429;2.3 final 和 override关键字&#x1f429;2.4 重载、重写、重定义对比 &#x1f431;3. 虚函数表&#x1f42f;4. 多态的原理&#x1f…

神经网络基础-神经网络补充概念-17-计算神经网络的输出

计算神经网络的输出通常涉及前向传播&#xff08;Forward Propagation&#xff09;的过程&#xff0c;其中输入数据通过网络的层级结构&#xff0c;逐步被传递并变换&#xff0c;最终生成预测结果。下面我将为你展示一个简单的神经网络前向传播的示例。 假设我们有一个具有以下…

【变形金刚01】attention和transformer所有信息

图1.来源&#xff1a;Arseny Togulev在Unsplash上的照片 一、说明 这是一篇 长文 &#xff0c;几乎讨论了人们需要了解的有关注意力机制的所有信息&#xff0c;包括自我注意、查询、键、值、多头注意力、屏蔽多头注意力和转换器&#xff0c;包括有关 BERT 和 GPT 的一些细节。因…

OpenCV图像处理——轮廓检测

目录 图像的轮廓查找轮廓绘制轮廓 轮廓的特征轮廓面积轮廓周长轮廓近似凸包边界矩形最小外接圆椭圆拟合直线拟合 图像的矩特征矩的概念图像中的矩特征 图像的轮廓 查找轮廓 binary,contours,hierarchycv.findContours(img,mode,method)绘制轮廓 cv.drawContours(img,coutours…

WSL2安装Ubuntu,配置机器学习环境

文章目录 1.WSL2安装Ubuntu&#xff0c;更改安装位置&#xff0c;作为开发环境供vscode和pycharm使用&#xff1a;2.更换国内源&#xff1a;3.安装图形界面&#xff1a;4.安装cudacudnntorch5.安装opencv6.调用摄像头7.使用yolov8测试 WSL全称Windows Subsystem for Linux&…

印度货代专线【我国到印度专线有哪些方式】

随着全球贸易的不断发展&#xff0c;我国与印度之间的贸易往来也日益频繁。作为两个人口最多的国家之一&#xff0c;中国和印度之间的货物运输需求不断增长。为了满足这一需求&#xff0c;印度货代专线应运而生&#xff0c;为进出口商提供高效、可靠的货物运输服务。本文将探索…

939. 最小面积矩形;2166. 设计位集;2400. 恰好移动 k 步到达某一位置的方法数目

939. 最小面积矩形 核心思想&#xff1a;枚举矩形的右边那条边的两个点&#xff0c;并用一个哈希表存储相同纵坐标的最近出现的列的列数,不断更新最近出现的左边那条边。 2166. 设计位集 核心思想&#xff1a;这题主要是时间复杂度的优化&#xff0c;用一个flag来标记当前翻转…

CSS自学框架之表单

首先我们看一下表单样式&#xff0c;下面共有5张截图 一、CSS代码 /*表单*/fieldset{border: none;margin-bottom: 2em;}fieldset > *{ margin-bottom: 1em }fieldset:last-child{ margin-bottom: 0 }fieldset legend{ margin: 0 0 1em }/* legend标签是CSS中用于定义…

IOS开发-XCode14介绍与入门

IOS开发-XCode14介绍与入门 1. XCODE14的小吐槽2. XCODE的功能bar一览3. XCODE项目配置一览4. XCODE更改DEBUG/RELEASE模式5. XCODE单元测试 1. XCODE14的小吐槽 iOS开发工具一直有个毛病&#xff0c;就是新版本的开发工具的总会有一些奇奇怪怪的bug。比如在我的Mac-Pro&#…