在Day 1提到的解法中使用了chars()
,這方法回傳為實作疊代器(Iterator)的型別Chars<’a>
,疊代器(Iterator)呼叫.next()
會依序取得char
型別的值,直到最後回傳None,因為會改變疊代器內部紀錄序列位置的狀態,Rust預設是不可變宣告,所以宣告疊代器變數時要宣告成可變 mut
。
impl Solution {
pub fn merge_alternately(word1: String, word2: String) -> String {
let mut new = String::new();
let mut iter1 = word1.chars();
let mut iter2 = word2.chars();
loop {
match (iter1.next(), iter2.next()) {
(Some(c1), Some(c2)) => {
new.push(c1);
new.push(c2);
}
(Some(c1), None) => {
new.push(c1);
new.extend(iter1);
break;
}
(None, Some(c2)) => {
new.push(c2);
new.extend(iter2);
break;
}
(None, None) => break,
}
}
new
}
}
來源:https://doc.rust-lang.org/std/primitive.str.html#method.chars
在範例程式中,我們看到Rust中疊代器是回傳Some<T>
或None
,因為Rust中沒有空值(Null),要表達空值的概念是用一個列舉Option<T>
來實現,有了這概念,編譯器在看到Option<T>
時才會處理數值是否為空的條件。
enum Option<T> {
None,
Some(T),
}
我們可以透過Match組成一系列的模式並依匹配到的模式
來控制流程,如1768. Merge Strings Alternately,我們組成四種模式,並定義每個模式要走的流程,最後回傳新字串。
疊代器1、2有值,依序把回傳字元加到新字串
疊代器1有值,疊代器2用完了,用extend()
把剩下的所有元素加到新字串
疊代器1用完了,疊代器2有值,用extend()
把剩下的所有元素加到新字串
都沒值,什麼都不做。
match (iter1.next(), iter2.next()) {
(Some(c1), Some(c2)) => {
new.push(c1);
new.push(c2);
}
(Some(c1), None) => {
new.push(c1);
new.extend(iter1);
break;
}
(None, Some(c2)) => {
new.push(c2);
new.extend(iter2);
break;
}
(None, None) => break,
}
今天我們要匹配的許多值來控制流程,可以先match目標值給狀態,再依狀態來控制流程,來刷個一題練習一下。
題目:將字串母音做相對位置交換,IceCreAm變成AceCreIm,leetcode變成leotcede
輸入:"IceCreAm"
輸出:"AceCreIm"
限制:
1≤字串長度≤3*10^5
字元都是ASCII能表達的
做法:先用.chars().collect()
將String轉成Vec<char>
、雙指針紀錄開始和結束的位置,判斷每個字元是否為母音,不是就移動一格,(false,_)
意思是只看第一個值,忽略第二個值不管,等找到兩個母音(true,true
)時做交換。
impl Solution {
pub fn reverse_vowels(s: String) -> String {
let mut chars: Vec<char> = s.chars().collect();
let (mut l, mut r) = (0, s.len()-1);
while l < r {
match (is_vowel(chars[l]), is_vowel(chars[r])) {
(false, _) => l += 1,
(_, false) => r -= 1,
(true, true) => {
chars.swap(l, r);
l += 1;
r -= 1;
}
}
}
chars.into_iter().collect()
}
}
fn is_vowel(c: char) -> bool {
match c {
'a' | 'e' | 'i' | 'o' | 'u'
| 'A' | 'E' | 'I' | 'O' | 'U' => true,
_ => false,
}
}
巨集可以想成Rust中的按鍵精靈,Rust幫你把指令寫好,在編譯期間幫你生成程式碼,省下手動寫的重複邏輯,為了可讀性我們寫了一個function is_vowel
做母音判斷並回傳布林值,可以用巨集matches!(val, pattern1 | pattern2),
將符合pattern的回傳true。
fn is_vowel(c: char) -> bool {
match c {
'a' | 'e' | 'i' | 'o' | 'u'
| 'A' | 'E' | 'I' | 'O' | 'U' => true,
_ => false,
}
}
//
fn is_vowel(c: char) -> bool {
matches!(
c,
'a' | 'e' | 'i' | 'o' | 'u' |
'A' | 'E' | 'I' | 'O' | 'U'
)
}
上述matches!
可能看不出來可以省多少工,我們來看如何用巨集宣告Vec<i32>
//沒使用巨集
let v = {
let mut temp = Vec::new();
temp.push(1);
temp.push(2);
temp.push(3);
temp.push(4);
temp.push(5);
temp
};
//使用巨集
let v = vec![1, 2, 3, 4, 5];
next()
方法拿下一個值Option<T>
判斷有沒有值