看了一下網上有關Rust的介紹,都說它的學習曲線至關陡峭,曾一度被其嚇着,後來發現Rust借鑑了Haskell等函數式編程語言的優勢,而我之前專門學習過Haskell,通過一段時間的入門學習,我如今已經喜歡上這門神奇的語言。算法
入門資料我用官方的《The Rust Programming Language》,很是權威,配合着《Rust by example》這本書一塊兒學習,效果很是不錯。編程
學習任何一項技能最怕沒有反饋,尤爲是學英語、學編程的時候,必定要「用」,學習編程時有一個很是有用的網站,它就是「歐拉計劃」,網址:https://projecteuler.netswift
這個網站提供了幾百道由易到難的數學問題,你能夠用任何辦法去解決它,固然主要還得靠編程,但編程語言不限,已經有Java、C#、Python、Lisp、Haskell等各類解法,固然直接用google搜索答案就沒意思了。數組
學習Rust最好先把基本的語法和特性看過一遍,而後就能夠動手解題了,解題的過程就是學習、試錯、再學習、掌握和鞏固的過程,學習進度會大大加快。微信
環境準備
在Windows下安裝,用官網上的rustup直接默認安裝便可。閉包
安裝完成以後,就有了《The Rust Programming Language》這本書的離線HTML版本,直接用命令打開:app
rustup doc --book
還要會使用強大的包管理器:cargo編程語言
這個cargo好用的另人髮指,建項目、編譯、運行都得用它:函數式編程
cargo new euler1
cd euler1
cargo build
cargo run
第一題
問題描述:函數
求1000之內(不含1000)的全部被3或5整除的整數之和。
直接上答案:
let mut sum = 0;
for i in 1..1000 {
if i % 3 == 0 || i % 5 == 0 {
sum += i;
}
}
println!("{}", sum);
mut關鍵字(mutable的縮寫)是Rust的一大特點,全部變量默認是不可變的,若是想可變,須要mut關鍵字,不然在 sum += i 時會報編譯錯誤。
println! 後面有一個歎號,表示這是一個宏,Rust裏的宏也是很是很是強大!如今還不到了解的時候。
學過Python的列表推導(List Comprehension)語法的感受這種題徹底能夠用一行語句搞定,Rust中須要用到filter()和sum()函數。
// 爲了方便閱讀,分紅多行
println!(
"{}",
(1..1000).filter(|x| x % 3 == 0 || x % 5 == 0)
.sum::<u32>()
);
.. 這個語法糖表示一個範圍,須要注意最後不包括1000,若是想包含1000,須要這樣寫:(1..=1000)
filter裏面的|x|定義了一個閉包函數,關於閉包,又是一個超級複雜的主題。
sum::<u32>() 是一個範型函數,這種兩個冒號的語法讓我好不適應。
還能夠用fold()函數,是這樣寫的:
println!(
"{}",
(1..1000)
.filter(|x| x % 3 == 0 || x % 5 == 0)
.fold(0, |s, a| s + a)
);
把這些數所有打印出來,注意打印語句裏的{:?}:
println!(
"{:?}",
(1..1000)
.filter(|x| x % 3 == 0 || x % 5 == 0)
.collect::<Vec<u32>>()
);
// [3, 5, 6, 9, 10, 12, ... 999]
第二題
問題描述:
斐波那契數列中,小於400萬的全部偶數之和。
算法並不難,這裏的數列以[1, 2]開始,後面每一個數是前面2個數字之和:
let mut fib = vec![1, 2];
let mut i = 2; // 已經有2個元素
let mut sum = 2;
loop {
let c = fib[i - 1] + fib[i - 2];
if c >= 4_000_000 {
break;
}
fib.push(c);
if c % 2 == 0 {
sum += c;
}
i += 1;
}
println!("{}", sum);
這裏沒有使用函數式編程,大量使用了mut,無限循環用loop語法。
rust中關於整數的表示提供了多種數據類型,默認的整數類型是i32,默認浮點類型是f64。
數字類型中比較有特色的是能夠用'_'分隔符,讓數字更容易讀一些,還能夠把u32, i64等類型做爲後綴來指明類型。
let 賦值語句與其它語言也不同,還能夠改變其類型,這個特性稱爲隱藏shadowing。
let x = 500u16;
let x = x + 1;
let x = 4_000_000_u64;
let x = "slb";
fib是一個向量,至關於其它語言裏的數組、列表。vec! 宏能夠對其進行初始化:
let mut fib = vec![1, 2];
上面一行與下面三行等價:
let mut fib = Vec::new();
fib.push(1);
fib.push(2);
push()函數用於給列表增長一個元素。
第三題
問題描述: 找出整數600851475143的最大素數因子。
素數就是隻能被1和自己整除的數,首先定義一個函數is_prime(),用於判斷是否爲素數:
fn is_prime(num: u64) -> bool {
for i in 2..(num / 2 + 1) {
if num % i == 0 {
return false;
}
}
true
}
Rust是強類型語言,看到函數定義裏的 -> bool,讓我想起了Haskell的語法。
函數最後一行的true孤零零的,沒有分號,讓人感受很奇怪。Rust是一個基於表達式的語言,一個語句塊的最後是一個表達式,固然也能夠用return true;
如今能夠查找最大的素數因子了:
let big_num = 600851475143;
for i in (2..=big_num).rev() {
if big_num % i == 0 && is_prime(i) {
println!("{}", i);
break;
}
}
程序編譯沒問題,但幾分鐘也運行不出來結果,試着把數字調小一點,好比:600851,不到1秒出來結果,看來程序的效率太差了,主要是有大量的判斷素數的運算量,須要優化。
嘗試把大數進行素數因子分解,而且把素因子記錄下來進行比較,效率獲得大幅提高,不到1秒得出結果。
let mut big_num = 600851475143;
let mut max_prime_factor = 2;
while big_num >= 2 {
for i in 2..=big_num {
if big_num % i == 0 && is_prime(i) {
big_num /= i;
if i > max_prime_factor {
max_prime_factor = i;
break;
}
}
}
}
println!("{}", max_prime_factor);
第四題
問題描述:
求兩個3位數之積最大的迴文數。
所謂迴文數,就是兩邊讀都同樣的數,好比:698896。
先寫一個判斷迴文數的函數:
fn is_palindromic(n: u64) -> bool {
let s = n.to_string();
s.chars().rev().collect::<String>() == s
}
這裏把數字轉換成字符串,再把字符串反序,若是兩個字符串同樣,則是迴文數。
Rust中字符串的反序操做好奇怪,居然不是s.rev(),我用google找到了那個代碼片斷。
剩下的邏輯並不複雜,用兩重循環能夠快速搞定。
let mut max = 0;
for x in 100..=999 {
for y in 100..=999 {
let prod = x * y;
if is_palindromic(prod) && prod > max {
max = prod;
// println!("{} x {} = {}", x, y, prod);
}
}
}
println!("{}", max);
我一開始覺得只要反序搜索就能夠快速找到答案,但找到的數並非最大,你能發現問題在哪嗎?不過,從這個錯誤代碼中,我學會了雙重循環如何跳出外層循環的語法。真是沒有白走的彎路。
// 錯誤代碼
'outer: for x in (100..=999).rev() {
for y in (100..=999).rev() {
let prod = x * y;
if is_palindromic(prod) {
println!("{} x {} = {}", x, y, prod);
break 'outer;
}
}
}
第五題
問題描述:
找出可以被1, 2, 3, ..., 20整除的最小整數。
代碼邏輯很簡單,一個一個嘗試整除,找到後跳出最外層循環。
let mut x = 2 * 3 * 5 * 7;
'outer: loop {
for f in 2..=20 {
if x % f != 0 {break;}
if f == 20 {
println!("{}", x);
break 'outer;
}
}
x += 2;
}
若是你感受程序運行效率不夠高,能夠用下面這個命令行,差異仍是很是大的,感受能夠與C程序的效率相媲美:
cargo run --release
第六題
問題描述: 求1到100天然數的「和的平方」與「平方和」的差。
用普通的過程式編程方法,這題太簡單,但要嘗試一下函數式編程思路,代碼能夠異常簡潔。
let sum_of_squares = (1..=100).map(|x| x*x).sum::<u32>();let sum = (1..=100).sum::<u32>();println!("{}", sum * sum - sum_of_squares);
另外還有一種使用fold()函數的寫法,理解起來更困難一些:
let sum_of_squares = (1..=100).fold(0, |s, n| s + n * n);let sum = (1..=100).fold(0, |s, n| s + n);println!("{}", sum * sum - sum_of_squares);
--- END ---
1539870_KBNiIXymh4SnmDEDZmUTg7tu1MTBVlLj
近期文章:
本文分享自微信公衆號 - Rust語言中文社區(rust-china)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。