# 整數運算
如上一章所述執行除錯版的 rtForth:
```
$ ./target/debug/examples/rf
```
或執行最佳化版本的 rtForth:
```
$ ./target/release/examples/rf
```
在 `rf>` 提示後輸入 `2 17 + .` 後按 Enter,
```
rf> 2 17 + .
19 ok
rf>
```
這兒發生了什麼事?首先 Forth 是直譯式語言,內建[直譯器](https://zh.wikipedia.org/wiki/%E7%9B%B4%E8%AD%AF%E5%99%A8) (英文:interpreter) 。當執行 `rf` 時,rtForth 會起動文本直譯器 (text interpreter) ,先印出 `rf>`,等待使用者輸入,再一個字 (word) 一個字的,從輸入緩衝區 (input buffer) 掃描 (scan) 使用者的輸入,在字典 (word list) 中查詢字的定義 (definition) 並執行。完成後顯示 `ok`,告訴使用者執行成功。若失敗則印出錯誤訊息。然後再印出提示字串 (prompt) `rf>` 請使用者繼續輸入。
```
文本直譯器 (Text interpreter)
Input buffer
2 1 7 + .
+--+--+--+--+--+--+--+--+--
|50|32|49|55|32|43|32|46|
+--+--+--+--+--+--+--+--+--
|
v
+-------+ yes +---------+
| Word? |-------->| Execute |----+
+-------+ +---------+ |
| no |
v +---------+ |
+---------+ yes | Number | |
| Number? |------>| on | |
+---------+ | stack | |
| no +---------+ |
v | |
+---------------+ | |
| Error message | | |
+---------------+ | |
| | |
Output buffer v v |
+---+---+----------- +---------+ |
|111|101| <-------| ok |<---+
+---+---+----------- +---------+
o k
```
Forth 的字和字是以空白或是換行符號隔開的。因此 `2 17 + .` 裡面一共有四個字。`2` 和 `17` 是數字。而 `+` 和 `.` 是已經在字典中定義好的指令 (instruction) 。
當直譯器無法在字典中找到這個字,比如 `2` 時,會將這個字轉換成數字 (number),存放在某記憶體中。這塊記憶體被稱為資料堆疊 (data stack)。稱為堆疊的原因是,這些轉成數字的資料會依次序排列,新來的被堆在舊的上面。就像下圖:
```
輸入緩衝區 (Input buffer) 內存放的數字是 2 17 + . 的 ASCII 碼。
+--+--+--+--+--+--+--+--+--
|50|32|49|55|32|43|32|46|
+--+--+--+--+--+--+--+--+--
2 1 7 + .
文本直譯器處理了 2 17 後的結果:
資料堆疊 (Data stack)
| |
+----+
| 17 | 疊頂(Top)
+----+
| 2 |
+----+
```
也可以以左右方式呈現資料堆疊,
```
資料堆疊 (Data stack)
+---+----+--
| 2 | 17 |
+---+----+--
疊頂
(Top)
```
或是為了方便,在文件中採用 `( 2 17 )` 這樣的註解呈現。
在上述例子中,`+` 從疊頂拿了兩個整數,`2` 和 `17`,相加後將結果 `19` 放回疊頂。所以堆疊的變化如下:
```
指令 + 的效果:
Data stack | |
+----+
| 17 | + | |
+----+ ===> +----+
| 2 | | 19 |
+----+ +----+
```
在文件中會以 `( 2 17 -- 19 )` 的註解方式呈現堆疊的效果。
最後的指令 `.` 從疊頂拿走了一個整數,將它轉成文字放在輸出緩衝區中。最後輸出緩衝區內的文字被顯示在螢幕上。
```
指令 . 的效果:
資料堆疊 (Data stack)
| | .
+----+ ===>
| 19 | | |
+----+ +----+
輸出緩衝區 (Output buffer) 內放的是 19 的 ASCII 碼
+--+--+--
|49|57|
+--+--+--
1 9
```
### 本節指令集
| 指令 | 堆疊效果及指令說明 | 口語唸法 |
|-----|----------------------------------------|--------|
| `+` | ( n1 n2 -- sum )   將資料堆疊上的最後的兩個整數 n1 n2 相加,將結果 sum 放回堆疊 | plus |
| `.` | ( n -- )   印出資料堆疊上最後的整數 n,並將它從堆疊上移除 | dot |
| `(` | ( -- )   註解,因為是指令,之後必須接一個空白。會忽略這空白之後一直到下一個右括弧 ) 之間的文字 | paren |
---------------
## 更多的整數四則運算
Forth 提供了許多整數運算的指令,要執行每個指令前,必須先把所需的資料先放進資料堆疊。這種先資料再指令的方式,被稱為後位法。而我們熟悉的四則運算的表示法則被稱為中位法。下表比較了中位法和後位法:
| 運算 | 中位法 | 後位法 |
|------|-------|-------|
| 加 |1 + 2 | 1 2 + |
| 減 |3 - 4 | 3 4 - |
| 乘 |5 * 6 | 5 6 * |
| 求商 |7 / 8 | 7 8 / |
| 求餘數|7 mod 8 | 7 8 mod |
| 求商數和餘數 | 不存在表示法 | 7 8 /mod |
實測以上運算如下:
```
rf> 1 2 + . 3 4 - . 5 6 * . 7 8 / . 7 8 mod .
3 -1 30 0 7 ok
```
在計算時,特意將 `1 2 + .` 和 `3 4 - .` 以二個空格隔開,這有點像是英文把句子分成片語,片語再分成單字。寫 Forth 的人常將幾個
Forth 指令構成的片語和片語用兩個或三個空格隔開,使得程式更容易閱讀。
`/mod` 指令在堆疊上留下兩個數字,餘數和商。因此需要兩個 `.` 將它們印出來。測試如下:
```
rf> 7 8 /mod . .
0 7 ok
```
注意在堆疊上的次序是 `( 餘數 商 )`。因此,第一個 `.` 會印出商,第二個會印出餘數。
### 本節指令集
| 指令 | 堆疊效果及指令說明 | 口語唸法 |
|-----|----------------------------------------|--------|
| `-` | ( n1 n2 -- diff )   將 n1 減去 n2。diff 是 n1, n2 的差 | minus |
| `*` | ( n1 n2 -- prod )   將 n1 乘以 n2。prod 是 n1, n2 的乘積 | star |
| `/` | ( n1 n2 -- quot )   將 n1 除以 n2。quot 是 n1 除以 n2 後的商數 | slash |
| `mod` | ( n1 n2 -- rem )   將 n1 除以 n2。rem 是 n1 除以 n2 後的餘數 | mod |
| `/mod` | ( n1 n2 -- rem quot )   將 n1 除以 n2。rem 是 n1 除以 n2 後的餘數,quot 是商數 | slash-mod |
------------------------
## 整數函式
Forth 提供了數學計算上常見的函式:
| 運算 | 函式 | 後位法 |
|------------|---------|-------|
| 求絕對值 | abs(-5) | -5 abs |
| 求加法反元素 | -(6) | 6 negate |
| 最小值 | min(7,8) | 7 8 min |
| 最大值 | max(9,0) | 9 0 max |
處理複雜的數學計算時,中位法依賴括弧決定指令執行的優先次序。後位法不使用括弧。由堆疊上資料的次序及及指令執行的次序決定。
以下以例子說明:
例一: 中位法 2 × (3 + 4) 和 (3 + 4) × 2
```
rf> 3 4 + 2 * .
14 ok
```
中位法的 2 × (3 + 4) 和 (3 + 4) × 2 都是先算 3 + 4,再乘以 2。所以後位法都是 `3 4 + 2 *` 。
例二:中位法 (2 × 3 - 4) / 5 + 6
```
rf> 2 3 * 4 - 5 / 6 + .
6 ok
```
注意這兒的除法 `/` 是整數除法,求的是整數的商數。如果使用浮點數運算,會得到 6.4。浮點數運算請見下一章。
例三:中位法 abs(2 - 6) / min(2, 6),也就是 2 - 6 的絕對值除以 2 和 6 的最小值。
```
rf> 2 6 - abs 2 6 min / .
2 ok
```
例四:中位法 -(3 + 4)<sup>2</sup>。
```
rf> 3 4 + 3 4 + * negate .
-49 ok
```
在此 `3 4 +` 計算了兩次,在未來章節會討論如何以程式簡化平方、立方的計算問題。
### 本節指令集
| 指令 | 堆疊效果及指令說明 | 口語唸法 |
|-----|----------------------------------------|--------|
| `abs` | ( n -- u )   u 是 n 的絕對值 | abs |
| `negate` | ( n1 -- n2 )   n2 是 n1 的加法反元素, n1+n2=0 | negate |
| `min` | ( n1 n2 -- n3 )   n3 是 n1 和 n2 中較小的數 | min |
| `max` | ( n1 n2 -- n3 )   n3 是 n1 和 n2 中較大的數 | max |
-------------
## 本章重點整理
* 文本直譯器 (text interpreter)
* 資料堆疊 (data stack)
* 堆疊效果 (stack effect):FORTH 文件常以 `( before -- after )` 的方式呈現執行指令時堆疊的變化。在 `--` 前的是指令執行前疊頂的內容。在 `--` 後的是執行後疊頂的內容。
* 字 (word)
* 指令 (instruction)
* 字典或指令集 (word list)
* 定義 (definition)
* 輸入緩衝區 (input buffer)
* 掃描 (scan)
* 輸出緩衝區 (output buffer)
* 整數 (integer)
-------------------------------------
## 本章指令集
| 指令 | 堆疊效果 | 說明 | 口語唸法 |
|-----|-------------------|-----------------------------|--------|
| `.` | ( n -- )   印出資料堆疊上最後的整數,並將它從堆疊上移除 | dot |
| `(` | ( -- )   註解,因為是指令,之後必須接一個空白。會忽略這空白之後一直到下一個右括弧 ) 之間的文字 | paren |
| `+` | ( n1 n2 -- n1+n2 )   將資料堆疊上的最後的兩個整數相加,結果放回堆疊 | plus |
| `-` | ( n1 n2 -- diff )   將 n1 減去 n2。diff 是 n1, n2 的差 | minus |
| `*` | ( n1 n2 -- prod )   將 n1 乘以 n2。prod 是 n1, n2 的乘積 | star |
| `/` | ( n1 n2 -- quot )   將 n1 除以 n2。quot 是 n1 除以 n2 後的商數 | slash |
| `mod` | ( n1 n2 -- rem )   將 n1 除以 n2。rem 是 n1 除以 n2 後的餘數 | mod |
| `/mod` | ( n1 n2 -- rem quot )   將 n1 除以 n2。rem 是 n1 除以 n2 後的餘數,quot 是商數 | slash-mod |
| `abs` | ( n -- u )   u 是 n 的絕對值 | a-b-s |
| `negate` | ( n1 -- n2 )   n2 是 n1 的加法反元素, n1+n2=0 | negate |
| `min` | ( n1 n2 -- n3 )   n3 是 n1 和 n2 中較小的數 | min |
| `max` | ( n1 n2 -- n3 )   n3 是 n1 和 n2 中較大的數 | max |