閱讀本篇文章你需要有編程邏輯的基礎,本篇文章將使用 Rust 編程語言的基本的條件控制語句和隨機數生成語句來實現一個兩位數的基礎數學加法計算命令行小游戲程序。這可能是 The Book 原書中的 猜數字 的演進版本,Rust 程序會生成兩個數的數字,用戶自己則計算答案輸入進去,然后由程序來判題。
準備工作
在編寫程序之前你的電腦必須先安裝好 Rust 的基礎開發環境,并且有 Cargo 支持,如果你是其他編程語言轉過來的例如 C、 Java 、Python 、JavaScript 、Swift 等,此程序將會很簡單不涉及復雜的操作。首先明白什么控制臺輸出、什么變量、知道什么是數學中的四則運算,和編程中條件控制語句和循環語句,這是看得懂本程序的前提,這個程序很簡單大概 100 行代碼左右,我很經量讓小白也能看得懂,整體的邏輯圖如下:
在編寫程序之前我們需要使用 cargo 創建一個名字為 math_game 的項目,使用命令如下:
$:?cargo?new?math_game??????????????????????????????? ?????Created?binary?(application)?`math_game`?package
到此創建完成了,打開項目目錄下面的 src/main.rs 源文件并且開始編寫程序主體邏輯代碼。
主體邏輯
首先我們要定義幾個全局的基礎變量,這些變量是幫助為我們來控制程序主體的邏輯的變量,具體的變量的作用已經在上方的注釋中說明了。值得注意的是 Rust 中默認聲明一個變量是不可變的,如果想在運行的過程中改變其值需要再變量定義的時候使用 mut 關鍵字,mut 關鍵字對應著 Mutable 的意思,如下:
fn?main()?{ ????//?當前回合數 ????let?mut?count?=?0; ????//?需要幾輪游戲 ????let?rounds?=?10; ????//?用了存儲分數 ????let?mut?score?=?0; ????//?生成兩個隨機數?默認值都為?0 ????let?mut?addend:?u32?=?0; ????let?mut?adding:?u32?=?0; }
接下來我們要定義兩個函數,分別來處理隨機數生成和并將隨機數轉成數學公式展示給用戶,下面定義一個函數,在 Rust 中定義一個函數要使用 fn 關鍵字。在 Rust 因為特殊的內存管理方式,所有權規則,默認在函數之間傳遞變量可能會導致變量的所有權被轉移,所以這里在函數簽名中采用的是可變借用的方式,具體什么是所有權規則和借用規則這不是本篇文章的重點,對應 Rust 初學者來說應該關注著怎么寫一個完整的小程序然后慢慢深入,函數體如下:
fn?next_math(addend:?&mut?u32,?adding:?&mut?u32)?->?u32?{ ????*addend?=?0; ????*adding?=?0; ????//?直接返回2個數的和方便結果判斷 ????*addend?+?*adding }
上面定義一個名為 next_math 的函數,參數列表分別為外部傳入的是存儲被加數和加數的變量,返回值則是這兩個數的之和。目前具體的隨機數生成邏輯還沒有寫,如果需要隨機數生成這時我們就使用第三方的 Crates,什么是 Crate ?Crate 可以認為是第三方開發者實現的一些類庫,類似于 Java 中的 jar 包,而 Crates 則是一個存放這些工具庫的中心倉庫,類似于 Java 中央倉庫 Maven。使用第三方的 rand 庫只需要在項目的根目錄中 cargo.toml 添加對應的依賴名稱:
[dependencies] rand = "0.8.3"
添加完成之后,需要使用 cargo update 更新一下項目,更新完成之后如果沒有錯誤,即可去代碼中完善 next_math 函數的邏輯,如下:
fn?next_math(addend:?&mut?u32,?adding:?&mut?u32)?->?u32?{ ????*addend?=?rand::thread_rng().gen_range(1..=100); ????*adding?=?rand::thread_rng().gen_range(1..=100); ????//?直接返回2個數的和方便結果判斷 ????*addend?+?*adding }
在函數中使用 rand::thread_rng().gen_range(1..=100) 即可生成 1 至 100 之間自然數,如果需要生成更大的數字只需要調整范圍即可,最后如果不寫 return 表示最后一行的結果作為返回值,返回值類型為 u32 類型,這里返回值充當著兩數之和并且返回給上層調用程序使用。
接下來要編寫一個提問函數 question 向用戶展示計算公式要求計算公式,傳入剛才通過 next_math 函數生成的數字,并且傳入題目的索引編號,如下:
//?向用戶展示計算公式要求計算公式 fn?question(addend:?&mut?u32,?adding:?&mut?u32,?index:?&mut?u32)?{ ????println!(":?第{index}題為?{addend}?+?{adding}?=???請輸入正確結果?",?index?=?*index?+?1,?addend?=?addend,?adding?=?adding); }
這里我們使用了 println! 這是一個 Rust 內置的宏,可以將字符串格式化輸出,在前段字符串中的 {} 則為后面所需要傳入的變量名稱占位符。接下來我們可以編寫程序主體邏輯了,要讓用戶不斷輸入信息,必須使用循環控制語句來編寫,在 Rust 中有多種循環語句,這里我們使用的是 while 循環語句,當條件滿足時會進入循環體執行里面邏輯,當不滿足時則跳出了循環體執行剩下的代碼邏輯,代碼邏輯如下:
use?std::Ordering; use?rand::Rng; use?std::io; fn?main()?{ ????//?當前回合數 ????let?mut?count?=?0; ????//?需要幾輪游戲 ????let?rounds?=?10; ????//?用了存儲分數 ????let?mut?score?=?0; ????//?生成兩個隨機數?默認值都為?0 ????let?mut?addend:?u32?=?0; ????let?mut?adding:?u32?=?0; ????println!(":?讓我們開始游戲吧!?一輪游戲為10個回合,每題10分,總分100分!"); ????//?這里我們使用?while?制造一個循環 ????while?count??num, ????????????//?如果非法設置默認值 ????????????Err(_)?=>?0, ????????}; ????????match?guess.cmp(&sum)?{ ????????????Ordering::Less?=>?{ ????????????????println!(":?你的答案太小了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Greater?=>?{ ????????????????println!(":?你的答案太大了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Equal?=>?{ ????????????????score?+=?10; ????????????????println!(":?恭喜你!答案正確!加10分!"); ????????????} ????????}; ????????//?添加輪數將其加一 ????????count?+=?1; ????} ????println!(":?本輪游戲結束!你的分數為?{score}?!{exp}", ?????????????score?=?score, ?????????????exp?=?if?score?>=?60 ?????????????{?"成績合格!"?} ?????????????else ?????????????{?"成績不合格!"?}) } fn?next_math(addend:?&mut?u32,?adding:?&mut?u32)?->?u32?{ ????*addend?=?rand::thread_rng().gen_range(1..=100); ????*adding?=?rand::thread_rng().gen_range(1..=100); ????//?直接返回2個數的和方便結果判斷 ????*addend?+?*adding } //?向用戶展示計算公式要求計算結構 fn?question(addend:?&mut?u32,?adding:?&mut?u32,?index:?&mut?u32)?{ ????println!(":?第{index}題為?{addend}?+?{adding}?=???請輸入正確結果?",?index?=?*index?+?1,?addend?=?addend,?adding?=?adding); }
這里跟要關注的是主體代碼邏輯里面函數作用,要讓用戶輸入信息那就必須使用系統調用從控制臺中讀入輸入的數據,使用兩段代碼邏輯完成,如下:
?
?
????????//?用來存儲用戶控制臺輸入的變量 ????????let?mut?guess?=?String::new(); ????????//?從控制臺上讀入輸入字符串 ????????io::stdin() ????????????.read_line(&mut?guess) ????????????.expect(":?你能不能好好輸!請輸入正數!");
第一段中的 guess 為用來存儲臨時用戶輸入的字符串信息,第二段邏輯則是通過標準庫中的 io 包從標準輸入中讀入一行輸入數據,而 expect 則防止用戶輸入的信息是非法,如果非法程序就提示該錯誤信息,非常容易理解。
????????//?解析輸入的值 ????????let?guess:?u32?=?match?guess.trim().parse()?{ ????????????Ok(num)?=>?num, ????????????//?如果非法設置默認值 ????????????Err(_)?=>?0, ????????}; ????????match?guess.cmp(&sum)?{ ????????????Ordering::Less?=>?{ ????????????????println!(":?你的答案太小了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Greater?=>?{ ????????????????println!(":?你的答案太大了!正確答案是?{sum}!",?sum?=?sum); ????????????} ????????????Ordering::Equal?=>?{ ????????????????score?+=?10; ????????????????println!(":?恭喜你!答案正確!加10分!"); ????????????} ????????};
最后我們得到用戶輸入的字符串,這時我們需要將字符串轉成數字,將字符串轉成數字的方式有很多種,這里使用的具有 Rust 特性的模式匹配的方式,如果字符串轉換成功將執行 Ok() 返回正常的數字,而如果字符串不能成功轉換成數字則會走 Err(_) ,這里如果轉換錯誤就使用默認值 0 作為用戶輸入的結果。
最后我們再次使用了模式匹配,將用戶輸入的結果和計算機計算的結果進行比對,在程序的頭部我們增加了另一個 use 聲明,從標準庫引入了一個叫做 std::Ordering 的類型到作用域中。Ordering 也是一個枚舉,不過它的成員是 Less、Greater 和 Equal。這是比較兩個值時可能出現的三種結果,接著,底部的五行新代碼使用了 Ordering 類型,cmp 方法用來比較兩個值并可以在任何可比較的值上調用。它獲取一個被比較值的引用:這里是把 guess 與 sum 做比較。然后它會返回一個剛才通過 use 引入作用域的 Ordering 枚舉的成員。使用一個 match 表達式,根據對 guess 和 sum 調用 cmp 返回的 Ordering 成員來決定接下來做什么。
有了上面的解釋就很容易明白這段代碼的邏輯了,用戶輸入正確的答案就將分數加 10 分,如果不正常可以告訴時候離準確的答案的差值或者大小,這里的使用的大小,最后完成整個程序的邏輯并且將游戲輪數加 1 回合,當循環體執行完成打印分數信息并且展示到控制臺中。
?println!(
????????":?本輪游戲結束!你的分數為?{score}?!{exp}", ????????score?=?score, ????????exp?=?if?score?>=?60?{ ????????????"成績合格!" ????????}?else?{ ????????????"成績不合格!" ????????} ????)
因為 Rust 沒有三目表達式,這里我采用的是if 語句塊來控制具體的分數合格信息的輸出。
小 結
至此我們就完成一個 Rust 小游戲的編寫工作,代碼其實一點都不復雜,主要是將思路轉換成具體代碼實現。
編輯:黃飛
?
評論
查看更多