堆疊是電腦科學中常用的一種集合類型。 它由兩個關鍵操作定義:push和pop。 push會把元素加到堆疊頂端。 pop會移除並回傳最頂端的元素。
把堆疊想像成一疊盤子。 你可以把盤子加到你那疊盤子的最上面,也可以拿走最上面那個盤子。 想拿到下面的盤子,就得先移開上面所有的盤子。
Rust 的向量實作std::vec::Vec可以用它的push()和pop()方法當作堆疊來用。
顧名思義,push()會把元素加到Vec的尾端,而pop()會移除並回傳最後一個元素。
這些操作可以非常快(在大 O 符號中是 O(1)),所以它們是用Vec最道地的方式之一。
堆疊很適合用來依特定順序存放任意數量的元素。 因為最後插入的元素會最先被回傳,所以堆疊通常被稱為 LIFO(後進先出,Last-In, First-Out)。 這種天生的順序可以用在很多地方,包括在計算逆波蘭表示法時追蹤狀態。
逆波蘭表示法(RPN)是一種撰寫數學運算式的方式。 與傳統的中序表示法不同,RPN 的運算子會跟在運算元後面。 舉例來說,你不會寫成:
2 + 2
而是寫成:
2 2 +
逆波蘭表示法最大的好處,就是它比中序表示法容易解析得多。 在複雜的運算式中,RPN 不需要考慮運算優先順序,也不需要括號。 例如:
(4 + 8) / (7 - 5)
可以寫成
4 8 + 7 5 - /
這兩種寫法的運算式求值結果都是 6。
我們來手動計算這個複雜的運算式吧。 如同簡介中所學到的,計算 RPN 需要用到堆疊。 這個堆疊用來存放運算子要處理的數值。 我們讓計算機從空堆疊開始,然後一次計算一個元素。
首先,我們遇到一個4,
所以把它推入剛建立好的堆疊。
4
接著,我們遇到一個8。
我們也把它推入堆疊。
4 8
現在,我們遇到一個+。
我們把最上面的兩個值(4 和 8)彈出,
將它們相加,
再把相加的結果推回堆疊。
12
我們對7、5和-做類似的事:
12 7
12 7 5
12 2
現在我們遇到一個/。
雖然我們上一個遇到的運算子是-,
但堆疊上還有兩個元素。
我們把這兩個元素彈出,
將它們相除,
再把結果推回堆疊。
6
最後,因為堆疊上剛好只剩一個元素, 我們可以說這個運算式的計算結果是 6。
你的目標是寫一個計算機,計算一連串以逆波蘭表示法排列的輸入,並回傳堆疊上最後的元素。
如果最後堆疊上不是剛好只有一個元素,請回傳None。
如果某個運算子的運算元太少(例如輸入2 +),請回傳None。
以下提供了一個列舉和一個待實作的函式作為起點。
#[derive(Debug)]
pub enum CalculatorInput {
Add,
Subtract,
Multiply,
Divide,
Value(i32),
}
pub fn evaluate(inputs: &[CalculatorInput]) -> Option<i32> {
todo!(
"Given the inputs: {inputs:?}, evaluate them as though they were a Reverse Polish notation expression"
);
}