最近想學習Libra數字貨幣的MOVE語言,發現它是用Rust編寫的,看來想準確理解MOVE的機制,還須要對Rust有深入的理解,因此開始了Rust的快速入門學習。算法
看了一下網上有關Rust的介紹,都說它的學習曲線至關陡峭,曾一度被其嚇着,後來發現Rust借鑑了Haskell等函數式編程語言的優勢,而我之前專門學習過Haskell,通過一段時間的入門學習,我如今已經喜歡上這門神奇的語言。編程
入門資料我用官方的《The Rust Programming Language》,很是權威,配合着《Rust by example》這本書一塊兒學習,效果很是不錯。數組
學習任何一項技能最怕沒有反饋,尤爲是學英語、學編程的時候,必定要「用」,學習編程時有一個很是有用的網站,它就是「歐拉計劃」,網址:
https://projecteuler.net閉包
這個網站提供了幾百道由易到難的數學問題,你能夠用任何辦法去解決它,固然主要還得靠編程,但編程語言不限,已經有Java、C#、Python、Lisp、Haskell等各類解法,固然直接用google搜索答案就沒意思了。編程語言
學習Rust最好先把基本的語法和特性看過一遍,而後就能夠動手解題了,解題的過程就是學習、試錯、再學習、掌握和鞏固的過程,學習進度會大大加快。函數式編程
在Windows下安裝,用官網上的rustup直接默認安裝便可。函數
安裝完成以後,就有了《The Rust Programming Language》這本書的離線HTML版本,直接用命令打開:oop
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::
還能夠用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()函數用於給列表增長一個元素。
還能夠改進,利用rust的延遲評價特性,有起始值無終止值的無限序列能夠用for語句搞定,原來的代碼能夠再精練一些,這種「2..」的語法在其它語言是沒法想像的。
let mut fib = vec![1, 2]; let mut sum = 2; for i in 2.. { let c = fib[i - 1] + fib[i - 2]; if c >= 4_000_000 { break; } fib.push(c); if c % 2 == 0 { sum += c; } } println!("{}", sum);
若是再使用函數式編程,還能夠更精練一點:
let mut fib = vec![1, 2]; for i in 2.. { let c = fib[i - 1] + fib[i - 2]; if c >= 4_000_000 { break; } fib.push(c); } println!("{}", fib.iter().filter(|&x| x % 2 == 0).sum::<u32>());
問題描述:
找出整數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);