# APCS實作題2019年2月第3題:函數運算式求值
> 日期:2023年10月24日
> 作者:王一哲
> 題目來源:108年2月實作題第3題
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=f640)
<br />
## 題目
### 問題描述
有三個函數:
$$
f(x) = 2x - 3 \\
g(x, y) = 2x + y - 7\\
h(x, y, z) = 3x - 2y + z
$$
另有一個由這三個函數所組成的運算式,依序給你其中的函數名稱及參數,請求出這個運算式的值。例如:
```
h f 5 g 3 4 3
```
代表
$$
h(f(5), g(3, 4), 3) = h(7, 3, 3) = 18
$$
<br />
### 輸入說明
輸入只有一行,含有運算式中所有的函數名稱及參數值,兩兩以一個空白隔開。函數名稱為 f、g、h 其中一個字母,參數值則為一個介於 $-1000$ 及 $1000$ 的整數。
<br />
### 輸出說明
輸出運算式的值。運算過程及結果的整數值其絕對值均不大於 $10^9$。
<br />
### 範例一:輸入
```
f f f 2
```
### 範例一:正確輸出
```
-5
```
<br />
### 範例二:輸入
```
h f 5 g 3 4 3
```
### 範例二:正確輸出
```
18
```
<br />
## Python 程式碼
### 方法1,使用堆疊
```python=
ss = input().split() # 由標準輸入讀取資料,存入串列 ss
nums = [] # 儲存數值及運算結果的串列
while ss: # 當 ss 還有元素時繼續執行
s = ss.pop() # 取出 ss 最後一個元素存入字串 s
if s == 'f': # 如果 s 為 'f'
x = nums.pop() # 取出 nums 最後一個元素存入變數 x
nums.append(2*x - 3) # 計算 f(x) 並加到 nums 最後面
elif s == 'g': # 如果 s 為 'g'
x = nums.pop() # 取出 nums 最後一個元素存入變數 x
y = nums.pop() # 取出 nums 最後一個元素存入變數 y
nums.append(2*x + y - 7) # 計算 g(x, y) 並加到 nums 最後面
elif s == 'h': # 如果 s 為 'h'
x = nums.pop() # 取出 nums 最後一個元素存入變數 x
y = nums.pop() # 取出 nums 最後一個元素存入變數 y
z = nums.pop() # 取出 nums 最後一個元素存入變數 z
nums.append(3*x - 2*y + z) # 計算 h(x, y, z) 並加到 nums 最後面
else: # 如果 s 不是 'f', 'g', 'h'
nums.append(int(s)) # 將 s 轉成整數並加到 nums 最後面
print(nums[0]) # 最後 nums 只剩下一個元素,就是運算結果
```
<br />
以範例二說明 while 迴圈運作流程:
1. 取出 ss 最後一個元素 3,轉成整數加到 nums 最後面,此時 ss 內容為 ['h', 'f', '5', 'g', '3', '4'],nums 內容為 [3]。
2. 取出 ss 最後一個元素 4,轉成整數加到 nums 最後面,此時 ss 內容為 ['h', 'f', '5', 'g', '3'],nums 內容為 [3, 4]。
3. 取出 ss 最後一個元素 3,轉成整數加到 nums 最後面,此時 ss 內容為 ['h', 'f', '5', 'g'],nums 內容為 [3, 4, 3]。
4. 取出 ss 最後一個元素 g,取出 nums 最後面兩個元素,$x = 3, y = 4$,計算 $g(3, 4) = 2 \times 3 + 4 - 7 = 3$,再將 3 加到 nums 最後面,此時 ss 內容為 ['h', 'f', '5'],nums 內容為 [3, 3]。
5. 取出 ss 最後一個元素 ,轉成整數加到 nums 最後面,此時 ss 內容為 ['h', 'f'],nums 內容為 [3, 3, 5]。
6. 取出 ss 最後一個元素 f,取出 nums 最後面一個元素,$x = 5$,計算 $f(5) = 2 \times 5 -3 = 7$,再將 7 加到 nums 最後面,此時 ss 內容為 ['h'],nums 內容為 [3, 3, 7]。
7. 取出 ss 最後一個元素 h,取出 nums 最後面三個元素,$x = 7, y = 3, z = 3$,計算 $h(7, 3, 3) = 3 \times 7 - 2 \times 3 + 3 = 18$,再將 3 加到 nums 最後面,此時 ss 已經沒有任何元素,nums 內容為 [18]。
於 ZeroJudge 測試結果,花費時間約為 19 ms,使用記憶體最多約為 3.3 MB,通過測試。
<br /><br />
### 方法2,使用函式遞迴
```python=
ss = input().split() # 由標準輸入讀取資料,存入串列 ss
def sol(): # 自訂函式,解題用的主要部分 sol
s = ss.pop(0) # 取出 ss 最後一個元素存入字串 s
if s == 'f': return f() # 如果 s 為 'f' 呼叫 f()
elif s == 'g': return g() # 如果 s 為 'g' 呼叫 g()
elif s == 'h': return h() # 如果 s 為 'h' 呼叫 h()
else: return int(s) # 如果 s 不是 'f', 'g', 'h',將 s 轉成整數
def f(): # 自訂函式 f()
x = sol() # 呼叫 sol(),找出要代入的 x 數值
return 2*x - 3 # 回傳 2*x -3 計算結果
def g(): # 自訂函式 g()
x = sol() # 呼叫 sol(),找出要代入的 x 數值
y = sol() # 呼叫 sol(),找出要代入的 y 數值
return 2*x + y - 7 # 回傳 2*x + y - 7 計算結果
def h(): # 自訂函式 h()
x = sol() # 呼叫 sol(),找出要代入的 x 數值
y = sol() # 呼叫 sol(),找出要代入的 y 數值
z = sol() # 呼叫 sol(),找出要代入的 z 數值
return 3*x - 2*y + z # 回傳 3*x - 2*y + z 計算結果
ans = sol() # 呼叫 sol() 計算答案
print(ans) # 印出答案
```
<br />
這個寫法是從國立中正大學資訊工程學系吳邦一教授編寫的 APCS 講義《[AP325](https://drive.google.com/drive/folders/10hZCMHH0YgsfguVZCHU7EYiG8qJE5f-m)》找到的,我只有稍微修改一下,將 sol() 中讀取資料的部分改成 s = ss.pop(0),刪掉記錄索引值用的全域變數 index。以範例二說明呼叫 sol() 的運作流程:
1. 取出 ss 最前方的元素 'h',呼叫 h(),進到 h()。
2. 於 h() 中呼叫 sol() 計算 x, y, z
3. 取出 ss 最前方的元素 'f',呼叫 h(),進到 f()。
4. 於 f() 中呼叫 sol() 計算 x
5. 取出 ss 最前方的元素 '5',轉成整數再回傳給 x。
6. 回到步驟4的 f(),計算 $f(5) = 2 \times 5 - 3 = 7$,回到步驟2的 h(),$x = 7$。
7. 回到步驟2的 h(),於 h() 中呼叫 sol() 計算 y
8. 取出 ss 最前方的元素 'g',呼叫 g(),進到 g()
9. 於 g() 中呼叫 sol() 計算 x, y
10. 取出 ss 最前方的元素 '3',轉成整數再回傳給 x
11. 取出 ss 最前方的元素 '4',轉成整數再回傳給 y
12. 回到步驟9的 g(),計算 $g(3, 4) = 2 \times 3 + 4 - 7 = 3$,回到步驟2的 h(),$y = 3$。
13. 回到步驟2的 h(),呼叫 sol() 計算 z
14. 取出 ss 最前方的元素 '3',轉成整數再回傳給 z。
15. 回到步驟2的 h(),計算 $h(7, 3, 3) = 3 \times 7 - 2 \times 3 + 3 = 18$。
於 ZeroJudge 測試結果,花費時間約為 18 ms,使用記憶體最多約為 3.3 MB,通過測試。
<br /><br />
## C++ 程式碼
### 方法1,使用堆疊
```cpp=
#include <iostream>
#include <string>
#include <stack>
using namespace std;
int main() {
string s; // 暫存資料的字串
stack<string> ss; // 儲存由標準輸入讀取資料用的堆疊
stack<int> nums; // 儲存數值及運算結果的堆疊
while(cin >> s) ss.push(s); // 由標準輸入讀取資料,先存入 s,再將 s 加到 ss 最上面
while(!ss.empty()) { // 當 ss 還有資料時繼續執行
s = ss.top(); ss.pop(); // 取出 ss 最上方的資料存入字串 s
if (s == "f") { // 如果 s 為 "f"
int x = nums.top(); nums.pop(); // 取出 nums 最上方的資料存入變數 x
nums.push(2*x - 3); // 計算 f(x) 並加到 nums 最上方
} else if (s == "g") { // 如果 s 為 "g"
int x = nums.top(); nums.pop(); // 取出 nums 最上方的資料存入變數 x
int y = nums.top(); nums.pop(); // 取出 nums 最上方的資料存入變數 y
nums.push(2*x + y - 7); // 計算 g(x, y) 並加到 nums 最上方
} else if (s == "h") { // 如果 s 為 "h"
int x = nums.top(); nums.pop(); // 取出 nums 最上方的資料存入變數 x
int y = nums.top(); nums.pop(); // 取出 nums 最上方的資料存入變數 y
int z = nums.top(); nums.pop(); // 取出 nums 最上方的資料存入變數 z
nums.push(3*x - 2*y + z); // 計算 h(x, y, z) 並加到 nums 最上方
} else { // 如果 s 不是 "f", "g", "h"
nums.push(stoi(s)); // 將 s 轉成整數並加到 nums 最上方
}
}
cout << nums.top() << endl; // 最後 nums 只剩下一個元素,就是運算結果
return 0;
}
```
<br />
由 Python 程式碼方法1改寫而成,於 ZeroJudge 測試結果,花費時間約為 3 ms,使用記憶體最多約為 352 kB,通過測試。
<br /><br />
### 方法2,使用函式遞迴
```cpp=
#include <iostream>
#include <string>
using namespace std;
int sol() { // 自訂函式,解題用的主要部分 sol
int x, y, z; // 整數變數 x, y, z
string s; cin >> s; // 由標準輸入讀取資料存入字串 s
if (s == "f") { // 如果 s 為 "f"
x = sol(); // 呼叫 sol(),找出要代入的 x 數值
return 2*x - 3; // 回傳 2*x -3 計算結果
} else if (s == "g") { // 如果 s 為 "g"
x = sol(); // 呼叫 sol(),找出要代入的 x 數值
y = sol(); // 呼叫 sol(),找出要代入的 y 數值
return 2*x + y - 7; // 回傳 2*x + y - 7 計算結果
} else if (s == "h") { // 如果 s 為 "h"
x = sol(); // 呼叫 sol(),找出要代入的 x 數值
y = sol(); // 呼叫 sol(),找出要代入的 y 數值
z = sol(); // 呼叫 sol(),找出要代入的 z 數值
return 3*x - 2*y + z; // 回傳 3*x - 2*y + z 計算結果
} else { // 如果 s 不是 "f", "g", "h"
return stoi(s); // 將 s 轉成整數並回傳
}
}
int main() {
cout << sol() << endl;
return 0;
}
```
<br />
這個寫法是從國立中正大學資訊工程學系吳邦一教授編寫的 APCS 講義《[AP325](https://drive.google.com/drive/folders/10hZCMHH0YgsfguVZCHU7EYiG8qJE5f-m)》找到的,我只有稍微修改一下,改成引用 string 函式庫,用 string 儲存讀取的資料,用 stoi 將字串轉成整數。於 ZeroJudge 測試結果,花費時間約為 3 ms,使用記憶體最多約為 328 kB,通過測試。
<br /><br />
---
###### tags:`APCS`、`C++`、`Python`