用 Rust 自动化无聊的事情(第二部分)

科技   2024-11-21 17:39   广东  

本系列博客文章旨在帮助正在阅读 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(01):
            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 倍。


Rust编程笔记
与你一起在Rust的世界里探索、学习、成长!
 最新文章