整數運算

如上一章所述執行除錯版的 rtForth:

$ ./target/debug/examples/rf

或執行最佳化版本的 rtForth:

$ ./target/release/examples/rf

rf> 提示後輸入 2 17 + . 後按 Enter,

rf> 2 17 + .
19  ok
rf>

這兒發生了什麼事?首先 Forth 是直譯式語言,內建直譯器 (英文: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 + . 裡面一共有四個字。217 是數字。而 +. 是已經在字典中定義好的指令 (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 ) 這樣的註解呈現。

在上述例子中,+ 從疊頂拿了兩個整數,217,相加後將結果 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)2

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

results matching ""

    No results matching ""