栈是计算机科学中常用的一种集合类型。 它由两个关键操作定义:push和pop。 push把一个元素添加到栈顶。 pop移除并返回最上面的元素。
可以把栈想象成一摞盘子。 你可以往这摞盘子的顶部再放一个盘子,也可以取走最上面的那个盘子。 要拿到下面更靠里的盘子,就得先拿走它上面所有的盘子。
Rust 的向量实现 std::vec::Vec可以通过它的push()和pop()方法当作栈来用。
自然,push()把一个元素添加到Vec的末尾,而pop()移除并返回最后一个元素。
这些操作可以非常快(在大 O 表示法中为 O(1)),
因此它们是最符合习惯的Vec用法之一。
栈很适合按特定顺序保存任意数量的元素。 因为最后插入的元素最先被返回, 所以栈通常被称为LIFO(后进先出)。 这种固有的顺序有很多用途, 包括在求值逆波兰表示法时跟踪状态。
逆波兰表示法(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"
);
}