【Rust】——改进I/O项目

💻博主现有专栏:

                C51单片机(STC89C516),c语言,c++,离散数学,算法设计与分析,数据结构,Python,Java基础,MySQL,linux,基于HTML5的网页设计及应用,Rust(官方文档重点总结),jQuery,前端vue.js,Javaweb开发,Python机器学习等
🥏主页链接:

                Y小夜-CSDN博客

目录

🎯使用迭代器并去掉clone

🎯直接使用返回的迭代器

🎯使用Iterator trait代替代替索引

🎯使用迭代器适配器来使代码更简明

🎯选择循环或者迭代器

🎯性能对比:循环vs迭代器


🎯使用迭代器并去掉clone

        我们增加了一些代码获取一个 String slice 并创建一个 Config 结构体的实例,它们索引 slice 中的值并克隆这些值以便 Config 结构体可以拥有这些值。

impl Config {pub fn build(args: &[String]) -> Result<Config, &'static str> {if args.len() < 3 {return Err("not enough arguments");}let query = args[1].clone();let file_path = args[2].clone();let ignore_case = env::var("IGNORE_CASE").is_ok();Ok(Config {query,file_path,ignore_case,})}
}

        那时我们说过不必担心低效的 clone 调用了,因为将来可以对它们进行改进。好吧,就是现在!

        起初这里需要 clone 的原因是参数 args 中有一个 String 元素的 slice,而 build 函数并不拥有 args。为了能够返回 Config 实例的所有权,我们需要克隆 Config 中字段 query 和 file_path 的值,这样 Config 实例就能拥有这些值。

🎯直接使用返回的迭代器

打开 I/O 项目的 src/main.rs 文件,它看起来应该像这样:

fn main() {let args: Vec<String> = env::args().collect();let config = Config::build(&args).unwrap_or_else(|err| {eprintln!("Problem parsing arguments: {err}");process::exit(1);});// --snip--
}
fn main() {let config = Config::build(env::args()).unwrap_or_else(|err| {eprintln!("Problem parsing arguments: {err}");process::exit(1);});// --snip--
}

   env::args 函数返回一个迭代器!不同于将迭代器的值收集到一个 vector 中接着传递一个 slice 给 Config::build,现在我们直接将 env::args 返回的迭代器的所有权传递给 Config::build

        不能编译因为我们还需更新函数体:

impl Config {pub fn build(mut args: impl Iterator<Item = String>,) -> Result<Config, &'static str> {// --snip--

   env::args 函数的标准库文档显示,它返回的迭代器的类型为 std::env::Args,同时这个类型实现了 Iterator trait 并返回 String 值。

        因为我们拥有 args 的所有权,并且将通过对其进行迭代来改变 args ,所以我们可以将 mut 关键字添加到 args 参数的规范中以使其可变。

🎯使用Iterator trait代替代替索引

        接下来,我们将修改 Config::build 的内容。因为 args 实现了 Iterator trait,因此我们知道可以对其调用 next 方法!

impl Config {pub fn build(mut args: impl Iterator<Item = String>,) -> Result<Config, &'static str> {args.next();let query = match args.next() {Some(arg) => arg,None => return Err("Didn't get a query string"),};let file_path = match args.next() {Some(arg) => arg,None => return Err("Didn't get a file path"),};let ignore_case = env::var("IGNORE_CASE").is_ok();Ok(Config {query,file_path,ignore_case,})}
}

        请记住 env::args 返回值的第一个值是程序的名称。我们希望忽略它并获取下一个值,所以首先调用 next 并不对返回值做任何操作。之后对希望放入 Config 中字段 query 调用 next。如果 next 返回 Some,使用 match 来提取其值。如果它返回 None,则意味着没有提供足够的参数并通过 Err 值提早返回。对 file_path 值进行同样的操作。

🎯使用迭代器适配器来使代码更简明

I/O 项目中其他可以利用迭代器的地方是 search 函数

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {let mut results = Vec::new();for line in contents.lines() {if line.contains(query) {results.push(line);}}results
}

        可以通过使用迭代器适配器方法来编写更简明的代码。这也避免了一个可变的中间 results vector 的使用。函数式编程风格倾向于最小化可变状态的数量来使代码更简洁。去掉可变状态可能会使得将来进行并行搜索的增强变得更容易,因为我们不必管理 results vector 的并发访问。

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {contents.lines().filter(|line| line.contains(query)).collect()
}

        回忆 search 函数的目的是返回所有 contents 中包含 query 的行。类似于示例 13-16 中的 filter 例子,可以使用 filter 适配器只保留 line.contains(query) 返回 true 的那些行。接着使用 collect 将匹配行收集到另一个 vector 中。这样就容易多了!尝试对 search_case_insensitive 函数做出同样的使用迭代器方法的修改吧。

🎯选择循环或者迭代器

        接下来的逻辑问题就是在代码中应该选择哪种风格:大部分 Rust 程序员倾向于使用迭代器风格。开始这有点难以理解,不过一旦你对不同迭代器的工作方式有了感觉之后,迭代器可能会更容易理解。相比摆弄不同的循环并创建新 vector,(迭代器)代码则更关注循环的目的。这抽象掉那些老生常谈的代码,这样就更容易看清代码所特有的概念,比如迭代器中每个元素必须面对的过滤条件。

🎯性能对比:循环vs迭代器

        为了决定使用哪个实现,我们需要知道哪个版本的 search 函数更快一些:是直接使用 for 循环的版本还是使用迭代器的版本。

        我们运行了一个性能测试,通过将阿瑟·柯南·道尔的“福尔摩斯探案集”的全部内容加载进 String 并寻找其中的单词 “the”。如下是 for 循环版本和迭代器版本的 search 函数的性能测试结果:

test bench_search_for  ... bench:  19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench:  19,234,900 ns/iter (+/- 657,200)

        结果迭代器版本还要稍微快一点!这里我们将不会查看性能测试的代码,我们的目的并不是为了证明它们是完全等同的,而是得出一个怎样比较这两种实现方式性能的基本思路。

        作为另一个例子,这里有一些取自于音频解码器的代码。解码算法使用线性预测数学运算(linear prediction mathematical operation)来根据之前样本的线性函数预测将来的值。这些代码使用迭代器链对作用域中的三个变量进行某种数学计算:一个叫 buffer 的数据 slice、一个有 12 个元素的数组 coefficients、和一个代表位移位数的 qlp_shift。例子中声明了这些变量但并没有提供任何值;虽然这些代码在其上下文之外没有什么意义,不过仍是一个简明的现实中的例子,来展示 Rust 如何将高级概念转换为底层代码:

let buffer: &mut [i32];
let coefficients: [i64; 12];
let qlp_shift: i16;for i in 12..buffer.len() {let prediction = coefficients.iter().zip(&buffer[i - 12..i]).map(|(&c, &s)| c * s as i64).sum::<i64>() >> qlp_shift;let delta = buffer[i];buffer[i] = prediction as i32 + delta;
}

        为了计算 prediction 的值,这些代码遍历了 coefficients 中的 12 个值,使用 zip 方法将系数与 buffer 的前 12 个值组合在一起。接着将每一对值相乘,再将所有结果相加,然后将总和右移 qlp_shift 位。

        像音频解码器这样的程序通常最看重计算的性能。这里,我们创建了一个迭代器,使用了两个适配器,接着消费了其值。那么这段 Rust 代码将会被编译为什么样的汇编代码呢?好吧,在编写本书的这个时候,它被编译成与手写的相同的汇编代码。遍历 coefficients 的值完全用不到循环:Rust 知道这里会迭代 12 次,所以它“展开”(unroll)了循环。展开是一种将循环迭代转换为重复代码,并移除循环控制代码开销的代码优化技术。

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

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

相关文章

【算法基础】选择排序与冒泡排序的思想与实现

文章目录 1. 选择排序1.1 思想1.2 实现 2. 冒泡排序2.1 思想2.2 实现 1. 选择排序 1.1 思想 选择排序的思想很简单&#xff0c;如上图所示。在每一次遍历子数组的过程中&#xff0c;选择最小的和子数组的第一位交换。子数组的选择从一开始的整个数组&#xff0c;到后面范围逐渐…

C++的stack和queue类(一):适配器模式、双端队列与优先级队列

目录 基本概念 适配器模式 stack.h test.cpp 双端队列-deque 仿函数 优先级队列 基本概念 1、stack和queue不是容器是容器适配器&#xff0c;它们没有迭代器 2、stack的quque的默认容器是deque&#xff0c;因为&#xff1a; stack和queue不需要遍历&#xff0…

保姆级教程带你实现HarmonyOS手语猜一猜元服务(三)

&#x1f680;四、元服务代码分析 &#x1f50e;1.元服务代码结构介绍 AppScope中存放应用全局所需要的资源文件。entry是应用的主模块&#xff0c;存放HarmonyOS应用的代码、资源等。oh_modules是工程的依赖包&#xff0c;存放工程依赖的源文件。build-profile.json5是工程级…

FreeRTOS学习 -- FreeRTOSConfig.h介绍

一、FreeRTOSConfig.h文件 FreeRTOS 的系统配置文件为 FreeRTOSConfig.h&#xff0c;在此配置文件中可以完成 FreeRTOS 的裁剪和配置。 /*FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.All rights reservedVISIT http://www.FreeRTOS.org TO ENSURE YOU AR…

Android Studio修改项目包名

1.第一步&#xff0c;项目结构是这样的&#xff0c;3个包名合在了一起&#xff0c;我们需要把每个包名单独展示出来 2.我们点击这个 取消选中后的包名结构是这样的&#xff0c;可以看到&#xff0c;包名的每个文件夹已经展示分开了&#xff0c;现在我们可以单独对每个包名文件夹…

Linux 中断处理

一、基本概念 1、中断及中断上下文 中断是一种由硬件设备产生的信号&#xff0c;不同设备产生的中断通过中断号来区分。CPU在接收到中断信号后&#xff0c;根据中断号执行对应的中断处理程序&#xff08;Interrupt Service Routine&#xff09; 内核对异常和中断的处理类似&a…

Linux mount用法

在Linux系统中&#xff0c;系统自动挂载了以下挂载点&#xff1a; /: xfs文件系统&#xff0c;根文件系统, 所有其他文件系统的挂载点。 /sys: sysfs文件系统&#xff0c;提供内核对象的信息和接口。 /proc: proc文件系统&#xff0c;提供进程和系统信息。 /dev: devtmpfs文件系…

qlabel 跑马灯

就是简单的定时器➕qlabel实现&#xff0c;思路&#xff1a;每一秒钟&#xff0c;把label上最右边的文字切下来&#xff0c;然后放在最左边&#xff0c;这样不断切&#xff0c;就相当于是文字轮转了

localhost 与 127.0.0.1 在本地开发中有什么区别

在进行前端开发时&#xff0c;我们经常使用localhost&#xff0c;只需运行npm run就能在浏览器中打开网页&#xff0c;地址栏显示类似于http://localhost:xxx/index.html&#xff0c;但是很多人并不了解更深层次的原理&#xff0c;不知道 localhost 与 127.0.0.1 到底有什么区别…

C++_priority_queue的学习

1.概述 1. 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的 2. 类似于堆&#xff0c;在堆中可以随时插入元素&#xff0c;并且只能检索最大堆元素 ( 优先队列中位于顶部的元 素 ) 3. 优先队列被实现为容器…

2024年商家争相入驻视频号小店,究竟有什么吸引力,新契机?

大家好&#xff0c;我是电商花花。 直播电商作为一种直观、生动的媒体形式&#xff0c;已经深入人心。 通过达人直播带货&#xff0c;短视频带货&#xff0c;商家可以更直观地向消费者展示产品&#xff0c;提升销售效果。 同时&#xff0c;视频号小店的开通&#xff0c;使得…

前端三剑客 —— JavaScript (第七节)

内容回顾 DOM编程 document对象 有属性 有方法 节点类型 元素节点 属性节点 文本节点 操作DOM属性 DOM对象.属性名称 DOM对象[属性名称] 调用DOM对象的API 操作DOM样式 获取有单位的样式值 标签对象.style.样式名称&#xff0c;这种方式只能操作行内样式。 使用getComputedSty…

vue3中的复制功能怎么做

很简单&#xff0c;比如想要复制p标签里面的1111&#xff0c;给他一个点击事件&#xff0c;然后加上navigator.clipboard.writeText即可完成。 说明&#xff1a; ​​​​​​​ navigator.clipboard 是 Web API 中的一个对象&#xff0c;它提供了对剪贴板的访问权限&…

创业公式(保罗·格雷厄姆)

保罗格雷厄姆有一套完整的创业哲学&#xff0c;他的创业公式是&#xff1a; &#xff08;1&#xff09;搭建原型 &#xff08;2&#xff09;上线运营&#xff08;别管bug&#xff09; &#xff08;3&#xff09;收集反馈 &#xff08;4&#xff09;调整产品 &#xff08;5&…

【ZZULIOJ】1050: 阶乘的累加和(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy code 题目描述 求1! 2! ……n! 输入 输入一个整数n&#xff0c;你可以假定n不大于10。 输出 输出一个整数&#xff0c;即阶乘累加的结果&#xff0c;单独占一行。 样例输入 Copy 4 样例输出 Copy 33 co…

基于51单片机的DAC0832锯齿波产生设计

**单片机设计介绍&#xff0c; 基于51单片机的DAC0832锯齿波产生设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的DAC0832锯齿波产生设计是一个结合了硬件与软件技术的项目&#xff0c;旨在利用51单片机和DAC08…

【机器学习】决策树(Decision Tree,DT)算法介绍:原理与案例实现

前言 决策树算法是机器学习领域中的一种重要分类方法&#xff0c;它通过树状结构来进行决策分析。决策树凭借其直观易懂、易于解释的特点&#xff0c;在分类问题中得到了广泛的应用。本文将介绍决策树的基本原理&#xff0c;包括熵和信息熵的相关概念&#xff0c;以及几种经典的…

如何申请公派访问学者?(全过程解析)

申请成为访问学者&#xff0c;无疑是增强个人学术实力与拓宽国际视野的绝佳机会。然而&#xff0c;对于初涉此道者&#xff0c;资金往往成为横亘在出国深造之路上的巨大障碍。幸运的是&#xff0c;CSC&#xff08;国家留学基金管理委员会&#xff09;的资助项目&#xff0c;为众…

现代深度学习模型和技术

Transformer模型的理解和应用 Transformer模型自2017年由Vaswani等人在论文《Attention is All You Need》中提出以来&#xff0c;已经彻底改变了自然语言处理&#xff08;NLP&#xff09;领域的面貌。Transformer的核心是自注意力&#xff08;Self-Attention&#xff09;机制…

liunx系统发布.net core项目

liunx系统发布.net core项目 准备.net6程序运行环境部署nginx&#xff0c;通过一个地址既能访问web api&#xff0c;又能访问web项目有一个客户把web api放到docker中&#xff0c;想通过nginx转发&#xff0c;nginx也支持配置多个程序api接口的其它 liunx系统&#xff1a;cento…