温度单位转换
简陋的转换: ```rust fn main() { let mut t: f64 = 0.;
let to_type = ‘c’;
t = trans_temperature(t, to_type); println!(“{}”, t); }
fn trans_temperature(t: f64, to_type: char) -> f64 {
if to_type == ‘c’ {
(t - 32.) / 1.8
} else {
t * 1.8 + 32.
}
}
2. 写得更结构化和 Rust 风格一些:
```rust
fn main() {
let mut t: Unit = Unit::Celsius(0.);
println!("given tempreture: {:?}", t);
// Celsius => Fahrenheit
t = trans_temperature(t);
println!("then trans to: {:?}", t);
// Fahrenheit => Celsius
t = trans_temperature(t);
println!("and trans back: {:?}", t);
let t = 100.;
println!("\nsimply combine `method` with `match pattern`:");
println!("{}℉ => {:?}", t, Unit::Fahrenheit(t).trans_temperature());
println!("{}°C => {:?}", t, Unit::Celsius(t).trans_temperature());
}
// 打印结果:
// given tempreture: Celsius(0.0)
// then trans to: Fahrenheit(-17.77777777777778)
// and trans back: Celsius(0.0)
//
// simply combine `method` with `match pattern`:
// 100℉ => Celsius(212.0)
// 100°C => Fahrenheit(37.77777777777778)
#[derive(Debug)]
enum Unit {
Celsius(f64),
Fahrenheit(f64),
}
fn trans_temperature(t: Unit) -> Unit {
match t {
Unit::Celsius(x) => Unit::Fahrenheit((x - 32.) / 1.8),
Unit::Fahrenheit(x) => Unit::Celsius(x * 1.8 + 32.),
}
}
impl Unit {
fn trans_temperature(self) -> Self {
match self {
Unit::Celsius(x) => Unit::Fahrenheit((x - 32.) / 1.8),
Unit::Fahrenheit(x) => Unit::Celsius(x * 1.8 + 32.),
}
}
}
斐波那契数列
简单、不完善并且低效的递归: ```rust fn main() { let n: u64 = 20; let fib: u64 = recurssive_fibonacci(n); println!(“n = {}, fib = {}”, n, fib);
let v = (1..n + 1).map(|x| recurssive_fibonacci(x)).collect::
>(); println!(“{:?}”, v); }
fn recurssive_fibonacci(n: u64) -> u64 { if n < 2 { n } else { recurssive_fibonacci(n - 1) + recurssive_fibonacci(n - 2) } }
2. 把 if 换成表达式的版本:[https://benjaminbrandt.com/fibonacci-in-rust/](https://benjaminbrandt.com/fibonacci-in-rust/)
```rust
fn recurssive_fibonacci(n: u32) -> u32 {
match n {
0 => 1,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
- 简单、速度更快一些的循环:
```rust
fn main() {
let n: u64 = 90;
let fib: u64 = for_fibonacci(n);
println!(“n = {}, fib = {}”, n, fib);
// 迭代器列表推导
let v = (1..n + 1).map(|x| for_fibonacci(x)).collect::
>(); println!(“{:?}”, v); }
fn forfibonacci(n: u64) -> u64 { let mut a: u64 = 0; let mut b: u64 = 1; let mut c: u64 = a + b; if n == 0 { println!(“n = {} not a positive number”, n); } else { for in 1..n { c = a + b; a = b; b = c; } } c }
4. ❤ 高效迭代器、结构体、trait:来自 [eliovir/rust-examples/fibonacci.rs](https://github.com/eliovir/rust-examples/blob/master/fibonacci.rs)
```rust
fn main() {
let n: usize = 90;
let fib: u64 = iterative_fibonacci().take(n).last().unwrap();
println!("n = {}, fib = {}", n, fib);
// 迭代器列表推导
let v = (1..n + 1) ‣v: Vec<u64> ‣Range<usize>
.map(|x| iterative_fibonacci().take(x).last().unwrap())
.collect::<Vec<u64>>()
println!("{:?}", v);
}
/// src: https://github.com/rust-lang/rust-by-example
struct Fibonacci {
curr: u64,
next: u64,
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let new_next = self.curr + self.next;
self.curr = self.next;
self.next = new_next;
Some(self.curr)
}
}
todo
- 整合三种函数到结构体和 trait 上
- 基准测试
这个版本的强大之处:
- 直观易读,利用宏编程创造“新语法”:
[ fib[n]: u64 = 0, 1 ... fib[n-1] + fib[n-2] ]
初始值和运算规则一目了然。 - 可以计算任意相邻两项表达式的结果。修改一个加号就能计算相减的结果
recurrence![ fib[n]: u64 = 0, 1 ... fib[n-1] - fib[n-2] ]
。 - 可以修改数据类型,让相邻两项的任何表达式作用于任何类型。比如小数
recurrence![ fib[n]: f64 = 0.0, 1.0 ... fib[n-1] - fib[n-2] ]
。 - 通过“重载”运算符,结合上面两项功能,定义你自己数据类型和运算规则。轻松实现复数版斐波那契数列。
- 背后是高效的迭代器,内存占用少、运算速度快。
```rust
[macro_use] extern crate recurrence;
fn main() { let fib = recurrence![ fib[n]: u64 = 0, 1 … fib[n-1] + fib[n-2] ]; let fib10: Vec<> = fib.take(10).collect(); assert_eq!(&*fib_10, &[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]); }
<a name="gavYv"></a>
# Twelve Days of Christmas
1. 笔者的写法:
```rust
fn main() {
let verse_ordinal: Vec<&str> =
"first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth"
.split(" ")
.collect();
let verse_cardinal: Vec<&str> = "a two three four five six seven eight nine ten elecen twelve"
.split(" ")
.collect();
let verse_stuff: Vec<&str> =
"partridge in a pear tree_turtle doves_french hens_calling birds_gold rings_geese a-laying_swans a-swimming_maids a-milking_ladies dancing_lords a-leaping_pipers piping_drummers drumming"
.split("_")
.collect();
let n: usize = 3;
// 测试第 n 天
// sing(n - 1, &verse_ordinal, &verse_cardinal, &verse_stuff);
// 从第 1 天到第 n 天
for i in 0..n {
sing(i, &verse_ordinal, &verse_cardinal, &verse_stuff);
}
}
fn sing(n: usize, vo: &Vec<&str>, vc: &Vec<&str>, vs: &Vec<&str>) {
println!("On the {} day of Christmas my true love gave to me:", vo[n]);
if n == 0 {
println!("{} {}.\n", vc[n], vs[n]);
} else {
for i in (0..n + 1).rev() {
if i > 0 {
println!("{} {},", vc[i], vs[i]);
} else {
println!("and {} {}.\n", vc[i], vs[i]);
}
}
}
}
打印结果:
On the first day of Christmas my true love gave to me:
a partridge in a pear tree.
On the second day of Christmas my true love gave to me:
two turtle doves,
and a partridge in a pear tree.
On the third day of Christmas my true love gave to me:
three french hens,
two turtle doves,
and a partridge in a pear tree.
数组结构拆分成业务逻辑的函数:开头、中间、结尾具有不同形式,联系的纽带是第 n 天
fn day_intro(n: u32) {
let day = match n {
1 => "first",
2 => "second",
3 => "third",
4 => "fourth",
5 => "fifth",
6 => "sixth",
7 => "seventh",
8 => "eighth",
9 => "ninth",
10 => "tenth",
11 => "eleventh",
12 => "twelfth",
_ => "",
};
println!(
"\nOn the {} day of Christmas\nmy true love sent to me:",
day
);
}
fn gift(n: u32, prefix: &str) {
let gift_text = match n {
1 => "a Partridge in a Pear Tree",
2 => "Two Turtle Doves",
3 => "Three French Hens",
4 => "Four Calling Birds",
5 => "Five Golden Rings",
6 => "Six Geese a Laying",
7 => "Seven Swans a Swimming",
8 => "Eight Maids a Milking",
9 => "Nine Ladies Dancing",
10 => "Ten Lords a Leaping",
11 => "Eleven Pipers Piping",
12 => "12 Drummers Drumming",
_ => "",
};
println!("{}{}", prefix, gift_text);
}
fn main() {
println!("TWELVE DAYS OF CHRISTMAS:");
for day in 1..13 {
day_intro(day);
for gift_day in (1..(day + 1)).rev() {
gift(
gift_day,
if gift_day == 1 && day != 1 {
"and "
} else {
""
},
);
}
}
}
vec 的平均数、中位数、众数
Cargo.toml
添加以下依赖:
[dependencies]
fraction = "0.8.0"
rand = "0.8.3"
src/main.rs
内容:
use fraction::{Fraction, ToPrimitive};
use rand::{thread_rng, Rng};
use std::collections::HashMap;
fn main() {
// let mut v: Vec<f64> = vec![1., -1., 100., 1., 36., 100.];
let mut v: Vec<f64> = (0..10).map(|_| thread_rng().gen_range(-5.0..5.0)).collect();
println!("the initial vec: \n{:?}\n", v);
println!("average: {}", average(&v));
println!("middle number: {}", median(&mut v));
let (mode_num, cnt) = mode(&v);
println!("mode number: {}, count: {}", mode_num, cnt);
}
fn average(v: &Vec<f64>) -> f64 {
v.iter().sum::<f64>() / v.len() as f64
}
fn median(v: &mut Vec<f64>) -> f64 {
v.sort_by(|a, b| a.partial_cmp(b).unwrap()); // f64 只支持 partial_cmp,注意 v 已经被排序了
// println!("the sorted vec: {:?}", v);
let pos1: usize = v.len() / 2;
if v.len() % 2 == 0 {
(v[pos1] + v[pos1 - 1]) / 2. // pos1 >= 1
} else {
v[pos1]
}
}
fn mode(v: &Vec<f64>) -> (f64, u64) {
let mut map = HashMap::new();
// 注意 Vec 对 f64/f32 不支持 `Eq`,无法直接计数,因此这里转换成支持 `Eq` 的 Fraction 类型
let f: Vec<Fraction> = v.iter().map(|&x| Fraction::from(x)).collect();
// 对 vec 元素计数
for ff in f {
*map.entry(ff).or_insert(0) += 1;
}
// 对 hashmap 按照 value (即个数)排序,多个最大值只返回最后一个
let item = map.iter().max_by_key(|entry| entry.1).unwrap();
(item.0.to_f64().unwrap(), *item.1)
}
- 最简单的版本是以
Vec<i32>
类型为范本,我也是参考了里面的代码框架:stackexchange: calculate-mean-median-and-mode-in-rust - 而我写的版本针对
Vec<f64>
类型,而f64
之类的浮点数不支持比较,即Ord
和Eq
not implemented,所以在数组排序和浮点元素计数上麻烦了一点。而这也正说明不同类型需要不同的处理方式,我们必须时刻思考和知道每一步的数据是什么类型,才能进行往后的处理。 当然,这么常见的需求肯定有现成的工具,比如这个对大型数据流支持基本的统计函数 crate:https://github.com/BurntSushi/rust-stats ,支持以下函数:
mean Compute the mean of a stream in constant space.
median Compute the exact median on a stream of data.
merge_all Merges all items in the stream.
mode Compute the exact mode on a stream of data.
modes Compute the modes on a stream of data.
stddev Compute the standard deviation of a stream in constant space.variance Compute the variance of a stream in constant space.
Cargo.toml
中的依赖:[dependencies]
streaming-stats = "0.2.3"
rand = "0.8.3"
src/main.rs
内容:use rand::{thread_rng, Rng};
use stats;
fn main() {
const N: usize = 20;
let v: Vec<i64> = (0..N).map(|_| thread_rng().gen_range(-5..6)).collect();
println!("the initial vec: \n{:?}\n", v);
println!("stats average: {}", stats::mean(v.iter().copied()));
println!("stats median: {:?}", stats::median(v.iter().copied()));
println!("stats modes: {:?}", stats::modes(v.iter().copied()));
println!("stats the exact mode: {:?}", stats::mode(v.iter().copied()));
println!("stats stddev: {:?}", stats::stddev(v.iter().copied()));
println!("stats variance: {:?}\n", stats::variance(v.iter().copied()));
let mut freq: stats::Frequencies<i64> = stats::Frequencies::new();
for ele in &v {
freq.add(*ele);
}
println!("频数 HashMap:{:?}", freq);
println!("元素个数:{:?}", freq.len());
println!("获取计数:{:?}", freq.count(&0));
println!("获取众数:{:?}", freq.mode());
println!("按元素及其计数降序返回元组:{:?}", freq.most_frequent());
println!("按元素及其计数升序返回元组:{:?}", freq.least_frequent());
}
打印结果:
the initial vec:
[-4, 5, -5, 1, 1, 3, -3, 2, 0, -3, -4, 5, 1, 2, -1, -3, 1, 4, 2, -1]
stats average: 0.14999999999999997
stats median: Some(1.0)
stats modes: [1]
stats the exact mode: Some(1)
stats stddev: 2.971110903349116
stats variance: 8.8275
频数 HashMap:{-3: 3, 2: 3, 3: 1, -1: 2, 4: 1, 0: 1, 5: 2, -5: 1, -4: 2, 1: 4}
元素个数:10
获取计数:1
获取众数:Some(1)
按元素及其计数降序返回元组:[(1, 4), (-3, 3), (2, 3), (-1, 2), (5, 2), (-4, 2), (3, 1), (4, 1), (0, 1), (-5, 1)]
按元素及其计数升序返回元组:[(3, 1), (4, 1), (0, 1), (-5, 1), (-1, 2), (5, 2), (-4, 2), (-3, 3), (2,3), (1, 4)]
pig_latin
将字符串转换为 Pig Latin,也就是每一个单词的第一个辅音字母被移动到单词的结尾并增加 “ay”,所以 “first” 会变成 “irst-fay”。元音字母开头的单词则在结尾增加 “hay”(“apple” 会变成 “apple-hay”)。
use std::io;
fn main() {
let mut word = input();
println!("{}", pig_latin(&mut word));
}
fn pig_latin(word: &mut String) -> String {
let pat: Vec<&str> = "a e i o u A E I O U".split(" ").collect();
let word_tail = word.split_off(1); // 注意此处直接修改了 word
if pat.contains(&word.as_str()) {
format!("{}{}-hay", word, word_tail)
} else {
format!("{}-{}ay", word_tail, word)
}
}
fn input() -> String {
let mut input_ = String::new();
io::stdin()
.read_line(&mut input_)
.expect("Failed to read from stdin");
input_.trim().to_string()
}
输入和查询部门成员
使用 HashMap 和 vector,创建一个文本接口来允许用户向公司的部门中增加员工的名字。例如,“Add Sally to Engineering” 或 “Add Amir to Sales”。接着让用户获取一个部门的所有员工的列表,或者公司每个部门的所有员工按照字典序排列的列表。
use std::collections::HashMap;
fn main() {
let mut info: HashMap<String, Vec<String>> = HashMap::new();
let s = "Add Sally to Engineering".to_string();
push_info(&mut info, &s);
let s = "Add Allice to Engineering".to_string();
push_info(&mut info, &s);
let s = "Add Allen to Engineering".to_string();
push_info(&mut info, &s);
let s = "Add Andy to Marketing".to_string();
push_info(&mut info, &s);
println!("{:#?}", info);
println!("choose the department below:\n{:?}", &info.keys());
let department = "Engineering".to_string();
println!(
"the sorted names in {}:\n{:?}",
department,
search_info(&info, &department)
);
}
fn parse_info(s: &String) -> [String; 2] {
let capture_name = s.find(" to ").unwrap();
let capture_department = capture_name + 4;
let name = s[4..capture_name].to_string();
let department = s[capture_department..].to_string();
[name, department] // 当然,也可以定义一个结构体
}
fn push_info(info: &mut HashMap<String, Vec<String>>, s: &String) {
let [name, department] = parse_info(s);
let vec_names = info.entry(department).or_insert(Vec::new());
vec_names.push(name);
}
fn search_info(info: &HashMap<String, Vec<String>>, department: &String) -> Vec<String> {
let mut vec_names = info.get(department).unwrap().to_vec();
vec_names.sort();
vec_names
}
打印结果:
{
"Marketing": [
"Andy",
],
"Engineering": [
"Sally",
"Allice",
"Allen",
],
}
choose the department below:
["Marketing", "Engineering"]
the sorted names in Engineering:
["Allen", "Allice", "Sally"]