本系列博客文章旨在帮助正在阅读 Rust 书籍并学习 Rust 的读者,通过实践来提高他们的知识水平。现在是时候进入第四章的挑战了。阅读第一部分
本章的前提条件
阅读 Rust 书中的“存储列表和值”章节。
逗号代码
假设你有一个这样的列表值:
spam = ['apples', 'bananas', 'tofu', 'cats']
编写一个函数,该函数接受一个列表值作为参数,并返回一个字符串,其中所有项目用逗号和空格分隔,最后一个项目之前插入“and”。例如,将上述spam
列表传递给函数将返回'apples, bananas, tofu, and cats'
。但是你的函数应该能够处理传递给它的任何列表值。确保测试传递空列表[]
的情况。
解决方案
Python 代码:
import time
start_time = time.time()
spam = [
"apples",
"bananas",
"tofu",
"cats",
"dogs",
"fish",
]
def comma_code(lst):
if len(lst) == 0:
return""
elif len(lst) == 1:
return lst[0]
else:
return", ".join(lst[:-1]) + " and " + lst[-1]
results = comma_code(spam)
end_time = time.time()
print(results)
elapsed_time = end_time - start_time
print(f"Time taken: {elapsed_time:.6f} seconds")
Rust 代码:
use std::time::Instant;
fn main() {
let start_time = Instant::now();
let spam = vec![
"apples",
"bananas",
"tofu",
"cats",
"dogs",
"fish",
];
fn comma_code(spam: &[&str]) -> String {
match spam.len() {
0 => String::new(),
1 => spam[0].to_string(),
_ => {
let capacity = spam.iter().map(|s| s.len()).sum::<usize>() + 2 * (spam.len() - 1) + 5;
letmut result = String::with_capacity(capacity);
letmut iter = spam.iter();
ifletSome(first) = iter.next() {
result.push_str(first);
}
let last = iter.next_back();
for item in iter {
result.push_str(", ");
result.push_str(item);
}
ifletSome(last_item) = last {
result.push_str(" and ");
result.push_str(last_item);
}
result
}
}
}
let result = comma_code(&spam);
let elapsed_time = start_time.elapsed();
println!("Result: {}", result);
println!("Time taken: {:.6} seconds", elapsed_time.as_secs_f64());
}
注意事项
可以进一步优化 Python 代码,例如计算数组容量。然而,这可能仅在处理非常大的数据集时才有效。在 Rust 中,优化代码是标准做法,声明String
的大小而不是创建空的String
是被认可的。Python 代码在可读性和简洁性上可能更胜一筹。
性能比较
(290 个字符串的数组/向量,平均运行 3 次)
Python: 0.000012 秒 Rust: 0.000006 秒
Rust 快两倍。
硬币翻转连续
问题陈述
编写一个程序来:
模拟抛硬币 100 次 记录“H”代表正面,“T”代表反面 重复实验 10,000 次 检查每个翻转列表中是否有连续六个正面或反面的情况 计算包含这种连续情况的实验百分比
解决方案
Python 代码:
import random, time
start_time = time.time()
numberOfStreaks = 0
for experimentNumber in range(10000):
flips = []
for i in range(100):
if random.randint(0, 1):
flips.append("H")
else:
flips.append("T")
for i in range(100 - 6):
if (
flips[i]
== flips[i + 1]
== flips[i + 2]
== flips[i + 3]
== flips[i + 4]
== flips[i + 5]
):
numberOfStreaks += 1
break
end_time = time.time()
print("Chance of streak: %s%%" % (numberOfStreaks / 100))
print("Elapsed time: %s seconds" % (end_time - start_time))
Rust 代码:
use rand::Rng;
use std::time::Instant;
fn main() {
let start_time = Instant::now();
letmut numberOfStreaks = 0;
for _ in0..10000 {
letmut flips = Vec::new();
for _ in0..100 {
let flip = if rand::thread_rng().gen_bool(0.5) {
"H"
} else {
"T"
};
flips.push(flip);
}
for i in0..flips.len() - 6 {
if flips[i..i + 6].iter().all(|&x| x == "H")
|| flips[i..i + 6].iter().all(|&x| x == "T")
{
numberOfStreaks += 1;
break;
}
}
}
let elapsed_time = start_time.elapsed();
println!("Chance of streak: {}%", numberOfStreaks / 100);
println!("Time taken: {:.6} seconds", elapsed_time.as_secs_f64());
}
性能比较
(平均运行 3 次)
Python: 0.266782 秒 Rust: 0.032330 秒
Rust 快 8.25 倍。
字符图片网格
问题陈述
给定一个列表的列表,其中每个值是一个字符字符串:
grid = [
['.', '.', '.', '.', '.', '.'],
['.', 'O', 'O', '.', '.', '.'],
['O', 'O', 'O', 'O', '.', '.'],
['O', 'O', 'O', 'O', 'O', '.'],
['.', 'O', 'O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O', 'O', '.'],
['O', 'O', 'O', 'O', '.', '.'],
['.', 'O', 'O', '.', '.', '.'],
['.', '.', '.', '.', '.', '.']
]
将grid[x][y]
视为用文本字符绘制的“图片”在 x 和 y 坐标处的字符。原点 (0, 0) 在左上角,x 坐标向右增加,y 坐标向下增加。复制之前的grid
值并编写代码使用它来打印图像。
解决方案
Python 代码:
import time
start_time = time.time()
grid = [
[".", ".", ".", ".", ".", "."],
[".", "O", "O", ".", ".", "."],
["O", "O", "O", "O", ".", "."],
["O", "O", "O", "O", "O", "."],
[".", "O", "O", "O", "O", "O"],
["O", "O", "O", "O", "O", "."],
["O", "O", "O", "O", ".", "."],
[".", "O", "O", ".", ".", "."],
[".", ".", ".", ".", ".", "."],
]
width = len(grid[0])
height = len(grid)
result = []
for col in range(width):
for row in grid:
result.append(row[col])
result.append("\n")
result_string = "".join(result)
end_time = time.time()
print(result_string)
elapsed_time = end_time - start_time
print(f"Time taken: {elapsed_time:.6f} seconds")
Rust 代码:
use std::time::Instant;
fn main() {
let start_time = Instant::now();
let picture_grid = vec![
vec!['.', '.', '.', '.', '.', '.'],
vec!['.', 'O', 'O', '.', '.', '.'],
vec!['O', 'O', 'O', 'O', '.', '.'],
vec!['O', 'O', 'O', 'O', 'O', '.'],
vec!['.', 'O', 'O', 'O', 'O', 'O'],
vec!['O', 'O', 'O', 'O', 'O', '.'],
vec!['O', 'O', 'O', 'O', '.', '.'],
vec!['.', 'O', 'O', '.', '.', '.'],
vec!['.', '.', '.', '.', '.', '.'],
];
let width = picture_grid[0].len();
let height = picture_grid.len();
letmut result = String::with_capacity(width * (height + 1));
for col in0..width {
for row in &picture_grid {
result.push(row[col]);
}
result.push('\n');
}
let elapsed_time = start_time.elapsed();
print!("{}", result);
println!("Time taken: {:.6} seconds", elapsed_time.as_secs_f64());
}
注意事项
在测试时,我意识到有时print!
宏被错误地包含在函数计时中,这会极大地减慢代码速度,以至于 Python 实际上在将打印到终端作为基准测试的一部分时更快,详情请参阅这里。
性能比较
Python: 0.000010 秒 Rust: 0.00000167 秒
Rust 大约快 6 倍。