by Phil Burk
中譯版本: 0.01.00
中譯者: Cnoize Chuang
這份教學文件的目的是提供一系列的經驗讓讀者認識 Forth 系統的主要概念. 這只是一個起點. 讀者可以不必依序閱讀. 從好奇心啟發的自由研究可能是學習任 何語言的最好方法. Forth 特別適合用於這種型式的學習方法.
The intent of this tutorial is to provide a series of experiments that will introduce you to the major concepts of Forth. It is only a starting point. Feel free to deviate from the sequences I provide. A free form investigation that is based on your curiosity is probably the best way to learn any language. Forth is especially well adapted to this type of learning.
這份教學文件是為了符合 ANS Forth 標準的 pforth 實作而寫的. 作者嘗試將這 份文件所使用的詞限制在 ANS 標準的部分, 但是有些 pforth 特有的詞可能已經 包含在其中了.
This tutorial is written for the PForth implementation of the ANS Forth standard. I have tried to restrict this tutorial to words that are part of the ANS standard but some PForth specific words may have crept in.
在這份教學文件中, 作者將用大寫英文字母來書寫讀者所需輸入的程式, 並且加以 縮排. 讀者可以用大寫英文字母或小寫來輸入. 在每一行的結束, 按 RETURN 或是 ENTER 鍵; 這將會讓 Forth 解譯讀者所輸入的程式.
In the tutorials, I will print the things you need to type in upper case, and indent them. You can enter them in upper or lower case. At the end of each line, press the RETURN (or ENTER) key; this causes Forth to interpret what you've entered.
Forth has one of the simplest syntaxes of any computer language. The syntax can be stated as follows, "Forth code is a bunch of words with spaces between them." This is even simpler than English! Each word is equivalent to a function or subroutine in a language like 'C'. They are executed in the order they appear in the code. The following statement, for example, could appear in a Forth program:
WAKE.UP EAT.BREAKFAST WORK EAT.DINNER PLAY SLEEP注意在 WAKE.UP 中有一個小數點. 這一個小數點在 Forth 編譯器中並沒有特別的意義. 作者只是簡單地用一個點來連接兩個英文單成讓它變成一個詞. Forth 的詞名可以是任 何字母、數字、或是標點符號的組合. 我們將會遇見下列這種名字型式的詞:
Notice that WAKE.UP has a dot between the WAKE and UP. The dot has no particular meaning to the Forth compiler. I simply used a dot to connect the two words together to make one word. Forth word names can have any combination of letters, numbers, or punctuation. We will encounter words with names like:
." #S SWAP ! @ ACCEPT . *它們都叫做 詞(words). 這個單字 "$%%-GL7OP" 也是合法的 Forth 詞, 雖然它的名字並不好. 程式設計員可以用合乎情理的方法來命名.
They are all called words. The word $%%-GL7OP is a legal Forth name, although not a very good one. It is up to the programmer to name words in a sensible manner.
現在是執行你的 Forth 並開始實驗的時間. 請查閱你的 Forth 手冊來取得如何 執行它的指示.
Now it is time to run your Forth and begin experimenting. Please consult the manual for your Forth for instructions on how to run it.
The Forth language is based on the concept of a stack. Imagine a stack of blocks with numbers on them. You can add or remove numbers from the top of the stack. You can also rearrange the order of the numbers. Forth uses several stacks. The DataStack is the one used for passing data between Forth words so we will concentrate our attention there. The Return Stack is another Forth stack that is primarily for internal system use. In this tutorial, when we refer to the "stack," we will be referring to the Data Stack.
堆疊一開始是空的. 要將一些數字放在堆疊上, 輸入:
The stack is initially empty. To put some numbers on the stack, enter:
23 7 9182讓我們現在利用 Forth 的詞 '.' 來印出在堆疊上的數字. 這個詞 '.' 是發音 成 "dot". 這是一個不容易寫在使用手冊上的詞, 因為它只是一個小數點.
Let's now print the number on top of the stack using the Forth word ' . ', which is pronounced " dot ". This is a hard word to write about in a manual because it is a single period.
輸入: .
Enter: .
你應該可以看見最後你輸入的數字: 9182, 被印出來了. Forth 有一個非常方便 的詞來顯示什麼東西在堆疊上. 它是 .S, 唸作 "dot S". 這個名字是由顯示 用的 '.' 和堆疊的 'S' 來組成的. (如果 TRACE-STACK 變數被設成 True 的話, PForth 將會自動在每一行的最後印出堆疊中的內容.) 如果你輸入:
You should see the last number you entered, 9182 , printed. Forth has a very handy word for showing you what's on the stack. It is .S , which is pronounced "dot S". The name was constructed from "dot" for print, and "S" for stack. (PForth will automatically print the stack after every line if the TRACE-STACK variable is set to TRUE.) If you enter:
.S你可以看見你的數字在一個列表中. 在最右邊的數字是在堆疊的最上方.
you will see your numbers in a list. The number at the far right is the one on top of the stack.
你可以注意到 9182 並不在堆疊上. '.' 詞將會在顯示堆疊上的數值時順便將它 移除. 相對地, '.S' 將不會修改堆疊的狀態.
You will notice that the 9182 is not on the stack. The word ' . ' removes the number on top of the stack before printing it. In contrast, ' .S ' leaves the stack untouched.
我們用 堆疊示意圖 的方式來標明這些詞在堆疊上運作的效果. 一個 堆疊示意圖包含在括號中. 在 Forth, 括號是代表註解. 在下面的例子中, 你不需要 輸入註解中的內容. 當然在你作程式設計時, 我們將會鼓勵使用註解和堆疊示意圖 來使得程式碼使容易理解. 在這份手冊中, 我們通常以粗體字來標明堆疊示意圖, 就像是下面的例子. 不要輸入這個程式. 一個用於 '.' 的堆疊示意圖就是如下所示:
We have a way of documenting the effect of words on the stack with a stack diagram. A stack diagram is contained in parentheses. In Forth, the parentheses indicate a comment. In the examples that follow, you do not need to type in the comments. When you are programming, of course, we encourage the use of comments and stack diagrams to make your code more readable. In this manual, we often indicate stack diagrams in bold text like the one that follows. Do not type these in. The stack diagram for a word like ' . ' would be:
. ( N -- , print number on top of stack )在 "--" 左邊的符號是表示在被這一詞處理的堆疊內容. 在這一個例子中, N 可以 代表任何的整數數字. 在 "--" 右邊直到 "," 中的內容, 是代表這個詞處理後, 堆疊上所留下的內容. 在這一個例子中是不存在的, 因為 'dot' 吃掉了傳進去的 N 值. (要注意這些堆疊的描敘是不必要的, 但是它們將會對於學習其他人的程式 有很大的幫助).
The symbols to the left of -- describe the parameters that a word expects to process. In this example, N stands for any integer number. To the right of --, up to the comma, is a description of the stack parameters when the word is finished, in this case there are none because 'dot' "eats" the N that was passed in. (Note that the stack descriptions are not necessary, but they are a great help when learning other peoples programs.)
在逗點後的文字是這一個詞的英文描述. 你將會注意到在 "--" 之後, N 就不見 了. 你也許會擔心事實上目前還有其他的數值在堆疊上, 是 23 和 7. 但是堆疊示意圖 只描敘這一個詞所影響的部分堆疊值. 在主詞彙表後有一個特別的小節來更仔細的描敘 堆疊示意圖.
The text following the comma is an English description of the word. You will note that after the -- , N is gone. You may be concerned about the fact that there were other numbers on the stack, namely 23 and 7 . The stack diagram, however, only describes the portion of the stack that is affected by the word. For a more detailed description of the stack diagrams, there is a special section on them in this manual right before the main glossary section.
在例子中, 你可能要清除堆疊. 你可以輸入 0SP, 發音為 "zero S P", 然後堆疊就會被清除了.
Between examples, you will probably want to clear the stack. If you enter 0SP, pronounced "zero S P", then the stack will be cleared.
由於堆疊是 Forth 的中心, 能夠簡單地改變堆疊是很重要的. 讓我們來看看 操作堆疊的更多詞.
Since the stack is central to Forth, it is important to be able to alter the stack easily. Let's look at some more words that manipulate the stack.
輸入:
Enter:
0SP .S \ That's a 'zero' 0, not an 'oh' O. 777 DUP .S你將會注意到有兩份 777 在堆疊上. 這一個詞 DUP 複製了在堆疊最上方 的物件. 如果你需要使用堆疊上的值但仍需要保留一份複本時, 這將會很有用. DUP 的堆疊示意圖是:
You will notice that there are two copies of 777 on the stack. The word DUP duplicates the top item on the stack. This is useful when you want to use the number on top of the stack and still have a copy. The stack diagram for DUP would be:
DUP ( n -- n n , 複製堆疊最上方的數值 )
DUP ( n -- n n , DUPlicate top of stack )另一個有用的詞, 是 SWAP. 輸入:
Another useful word, is SWAP. Enter:
0SP 23 7 .S SWAP .S SWAP .SDUP 的堆疊示意圖是:
The stack diagram for SWAP would be:
SWAP ( a b -- b a , 將堆疊最上面的兩個數值交換位置 )
SWAP ( a b -- b a , swap top two items on stack )現在輸入:
Now enter:
OVER .S OVER .S這一個詞 OVER 將會複製一份堆疊上第二筆元素的內容. 它的堆疊示意圖將是:
The word OVER causes a copy of the second item on the stack to leapfrog over the first. It's stack diagram would be:
OVER ( a b -- a b a , 複製一份堆疊上第二個元素 )
OVER ( a b -- a b a , copy second item on stack )
這邊有另一個常用的 Forth 詞:
Here is another commonly used Forth word:
DROP ( a -- , 將堆疊最上面的元素移除 )
DROP ( a -- , remove item from the stack )
你可以猜猜看我們輸入下列程式後會看到什麼:
Can you guess what we will see if we enter:
0SP 11 22 .S DROP .S另外一個操作堆疊的有用的詞是 ROT, 輸入:
Another handy word for manipulating the stack is ROT. Enter:
0SP 11 22 33 44 .S ROT .S因此 ROT 的堆疊示意圖是:
The stack diagram for ROT is, therefore:
ROT ( a b c -- b c a , 將堆疊中的第三個元素移到最上面 )
ROT ( a b c -- b c a , ROTate third item to top )
你現在已經學會更多的堆疊操作詞了. 你將會在幾乎每一個 Forth 程式中看到 它們. 我應該警告你如果你使用太多的堆疊操作詞在你的程式中, 則你應該重新 檢查你的程式碼, 也許應該重新組織它們. 你將可以發現你可以使用 local 或 是 global 變數 來避免過多的堆疊操作. 這些稍後將會討論到.
You have now learned the more important stack manipulation words. You will see these in almost every Forth program. I should caution you that if you see too many stack manipulation words being used in your code then you may want to reexamine and perhaps reorganize your code. You will often find that you can avoid excessive stack manipulations by using local or global VARIABLES which will be discussed later.
如果你要取得在堆疊上任意位置的元素, 使用 PICK. 試著輸入:
If you want to grab any arbitrary item on the stack, use PICK . Try entering:
0SP 14 13 12 11 10 3 PICK . ( prints 13 ) 0 PICK . ( prints 10 ) 4 PICK .PICK 會複製一份堆疊上的第 N 個元素. 而這個數字 N 是從 0 開始的, 因此:
PICK makes a copy of the Nth item on the stack. The numbering starts with zero, therefore:
0 PICK 與 DUP 相等
1 PICK 與 OVER 相等
PICK ( ... v3 v2 v1 v0 N -- ... v3 v2 v1 v0 vN )
(警告. Forth-79 和 FIG Forth 標準跟 ANS 和 Forth '83 標準不同, 它們是從 1 開始而不是 0.)
(Warning. The Forth-79 and FIG Forth standards differ from the ANS and Forth '83 standard in that their PICK numbering starts with one, not zero.)
我將其他有用的堆疊操作詞的堆疊示意圖包含如下. 將一些數字放進堆疊, 並且 呼叫它們來得到一些它們如何作用的感覺. 再次提醒, 在括號中的文字只是一些註解, 並且不需要將它們輸入.
I have included the stack diagrams for some other useful stack manipulation words. Try experimenting with them by putting numbers on the stack and calling them to get a feel for what they do. Again, the text in parentheses is just a comment and need not be entered.
DROP ( n -- , 移除堆疊最上方的元素 )
?DUP ( n -- n n | 0 , 如果非零則複製一份, '|' 意思是'或' )
-ROT ( a b c -- c a b , 將最上面的元素旋轉到第三個元素 )
2SWAP ( a b c d -- c d a b , 兩個一組的交換位置 )
2OVER ( a b c d -- a b c d a b , 兩個一組的跳取 )
2DUP ( a b -- a b a b , 兩個一組的複製 )
2DROP ( a b -- , 移除兩個元素 )
NIP ( a b -- b , 移除第二個元素 )
TUCK ( a b -- b a b , 將最上面的元素複製成第三個元素 )
---------------------------------------------------------
DROP ( n -- , remove top of stack )
?DUP ( n -- n n | 0 , duplicate only if non-zero, '|' means OR )
-ROT ( a b c -- c a b , rotate top to third position )
2SWAP ( a b c d -- c d a b , swap pairs )
2OVER ( a b c d -- a b c d a b , leapfrog pair )
2DUP ( a b -- a b a b , duplicate pair )
2DROP ( a b -- , remove pair )
NIP ( a b -- b , remove second item from stack )
TUCK ( a b -- b a b , copy top item to third position )
Start each problem by entering:
0SP 11 22 33然後用你已經學過的堆疊操作詞來讓最後堆疊上的數值跟問題中列出的相同:
Then use the stack manipulation words you have learned to end up with the following numbers on the stack:
1) 11 33 22 22
2) 22 33
3) 22 33 11 11 22
4) 11 33 22 33 11
5) 33 11 22 11 22問題的答案 可以在這份教學文件的結尾找到.
Great joy can be derived from simply moving numbers around on a stack. Eventually, however, you'll want to do something useful with them. This section describes how to perform arithmetic operations in Forth.
Forth 的算術運算工作是在目前堆疊的最上方運作的. 如果你要將堆疊最上方 的兩個數字加在一起, 使用這一個 Forth 詞 +, 發音成 "plus". 輸入:
The Forth arithmetic operators work on the numbers currently on top of the stack. If you want to add the top two numbers together, use the Forth word + , pronounced "plus". Enter:
2 3 + . 2 3 + 10 + .這種風格的算術表示法是叫做 後置表示法 或是 RPN. 如果你有 HP 的計算機, 你將會很熟悉. 在接下來的範例中, 我將會在註解中放置等價的 代數式子.
This style of expressing arithmetic operations is called Reverse Polish Notation, or RPN. It will already be familiar to those of you with HP calculators. In the following examples, I have put the algebraic equivalent representation in a comment.
其他的算數運算元是 - * /, 輸入:
Some other arithmetic operators are - * / . Enter:
30 5 - . ( 25=30-5 ) 30 5 / . ( 6=30/5 ) 30 5 * . ( 150=30*5 ) 30 5 + 7 / . \ 5=(30+5)/7有些運算的組合是非常常用的, 而為了加快速度, 它們已經被直接使用組合語言 重寫了. 例如, 2* 是 2 * 的縮寫. 你應該在需要增加你的程式的速度時 使用它們. 它們包括:
Some combinations of operations are very common and have been coded in assembly language for speed. For example, 2* is short for 2 * . You should use these whenever possible to increase the speed of your program. These include:
1+ 1- 2+ 2- 2* 2/試著輸入:
Try entering:
10 1- . 7 2* 1+ . ( 15=7*2+1 )有一件你應該要注意的事情是當你使用 / 來對於整數作運算時, 餘數將會消失. 輸入:
One thing that you should be aware of is that when you are doing division with integers using / , the remainder is lost. Enter:
15 5 / . 17 5 / .這對於所有的語言在所有的電腦都是真的. 稍後我們將會探討 /MOD 和 MOD, 它們將會給我們除法之後的餘數.
This is true in all languages on all computers. Later we will examine /MOD and MOD which do give the remainder.
It's now time to write a small program in Forth. You can do this by defining a new word that is a combination of words we have already learned. Let's define and test a new word that takes the average of two numbers.
我們將會使用兩個新的詞 : ("colon"), 和 ; ("semicolon"). 這兩個詞開始和結束一個典型的 Forth 定義. 輸入:
: AVERAGE ( a b -- avg ) + 2/ ;恭喜. 你剛寫好了一個 Forth 程式. 讓我們看看剛才發生什麼了. 冒號告訴 Forth 增加一個新詞到詞的列表中. 這個列表叫做 Forth 詞典. 新詞的名字就是跟隨在 冒號後面的名字. 任何跟隨在這個名字後的 Forth 詞都將會被編譯進這個新詞. 這將會持續到遇到分號為止, 而它將會結束這一個定義.
Congratulations. You have just written a Forth program. Let's look more closely at what just happened. The colon told Forth to add a new word to its list of words. This list is called the Forth dictionary. The name of the new word will be whatever name follows the colon. Any Forth words entered after the name will be compiled into the new word. This continues until the semicolon is reached which finishes the definition.
讓我們輸入這一個詞來測試:
Let's test this word by entering:
10 20 AVERAGE . ( should print 15 )一旦這一個詞被定義了之後, 它可以用來被定義更多詞. 讓我們來寫一個詞來測試 我們的詞. 輸入:
Once a word has been defined, it can be used to define more words. Let's write a word that tests our word.. Enter:
: TEST ( --) 50 60 AVERAGE . ; TEST試著合併你已經學過的詞到你所選擇的新的 Forth 定義中. 如果你保證不受影響, 你可以利用輸入下列的詞來得到所有可用來設計程式的詞的列表:
Try combining some of the words you have learned into new Forth definitions of your choice. If you promise not to be overwhelmed, you can get a list of the words that are available for programming by entering:
WORDS別擔心, 這些詞中只有一小部分將會直接在你的程式中使用.
Don't worry, only a small fraction of these will be used directly in your programs.
When you need to know the remainder of a divide operation. /MOD will return the remainder as well as the quotient. the word MOD will only return the remainder. Enter:
0SP 53 10 /MOD .S 0SP 7 5 MOD .S另外兩個有用的詞是 MIN and MAX. 它們分別接受兩個輸入值, 並且回傳其中的最小值或是最大值. 試著輸入:
Two other handy words are MIN and MAX . They accept two numbers and return the MINimum or MAXimum value respectively. Try entering the following:
56 34 MAX . 56 34 MIN . -17 0 MIN .另外一些有用的詞是:
Some other useful words are:
ABS ( n -- abs(n) , n 的絕對值 )
NEGATE ( n -- -n , 取負值, 比 -1 * 快 )
LSHIFT ( n c -- n<<c, n 向左 shift c 位數 )
RSHIFT ( n c -- n>>c, n 邏輯右 shift c 位數 )
ARSHIFT ( n c -- n>>c, n 算術右 shift c 位數 ) -------------------------------------------------------------------
ABS ( n -- abs(n) , absolute value of n )
NEGATE ( n -- -n , negate value, faster then -1 * )
LSHIFT ( n c -- n<<c , left shift of n )
RSHIFT ( n c -- n>>c , logical right shift of n )
ARSHIFT ( n c -- n>>c ) , arithmetic right shift of n )
ARSHIFT 或 LSHIFT 可以用來快速進行 2 次方數值的乘法. 右 shift 一位數可以 用來做除以 2 之用. 這通常會比做一個正常的乘法或除法快. 試著輸入:
ARSHIFT or LSHIFT can be used if you have to multiply quickly by a power of 2 . A right shift is like doing a divide by 2. This is often faster than doing a regular multiply or divide. Try entering:
: 256* 8 LSHIFT ; 3 256* .
If you are having problems with your calculation overflowing the 32-bit precision of the stack, then you can use */ . This produces an intermediate result that is 64 bits long. Try the following three methods of doing the same calculation. Only the one using */ will yield the correct answer, 5197799.
34867312 99154 * 665134 / . 34867312 665134 / 99154 * . 34867312 99154 665134 */ .
How do we express complex algebraic expressions in Forth? For example: 20 + (3 * 4)
要轉換這一個式子, 你必須把運算的順序照著實際執行的順序排列. 因此, 在 Forth 中, 看起來將會是:
To convert this to Forth you must order the operations in the order of evaluation. In Forth, therefore, this would look like:
3 4 * 20 +執行順序在 Forth 中是從左至右的, 所以並不會有模稜兩可的情形. 比較下列的 代數式和它們的 Forth 等價式: (不要輸入這些)
Evaluation proceeds from left to right in Forth so there is no ambiguity. Compare the following algebraic expressions and their Forth equivalents: (Do not enter these!)
(100+50)/2 ==> 100 50 + 2/ ((2*7) + (13*5)) ==> 2 7 * 13 5 * +如果有任何的式子困擾你, 試著一次只輸入一個詞, 並且每次都用 .S 來檢查堆疊.
If any of these expressions puzzle you, try entering them one word at a time, while viewing the stack with .S .
Convert the following algebraic expressions to their equivalent Forth expressions. (Do not enter these because they are not Forth code!)
(12 * ( 20 - 17 ))
(1 - ( 4 * (-18) / 6) )
( 6 * 13 ) - ( 4 * 2 * 7 )用你已經學過的詞來寫這些新詞:
Use the words you have learned to write these new words:
SQUARE ( N -- N*N , 計算平方 )
DIFF.SQUARES ( A B -- A*A-B*B , 平方差值 )
AVERAGE4 ( A B C D -- [A+B+C+D]/4 , 4 個數值的平均 )
HMS>SECONDS ( HOURS MINUTES SECONDS -- TOTAL-SECONDS , 轉換時間 )
SQUARE ( N -- N*N , calculate square )
DIFF.SQUARES ( A B -- A*A-B*B , difference of squares )
AVERAGE4 ( A B C D -- [A+B+C+D]/4 )
HMS>SECONDS ( HOURS MINUTES SECONDS -- TOTAL-SECONDS , convert )問題的答案可以在這份教學文件的結尾找到.
The numbers on top of the stack can represent anything. The top number might be how many blue whales are left on Earth or your weight in kilograms. It can also be an ASCII character. Try entering the following:
72 EMIT 105 EMIT你將看見 "Hi" 在 OK 之前顯示出來. 72 在 ASCII 中是代表 'H' 字元, 而 105 是 'i' 字元. EMIT 會取出堆疊中的數字, 並且以字元的型式輸出. 如果你要找到任何 字元的 ASCII 值, 你可以使用 "ASCII" 這個詞. 輸入:
You should see the word "Hi" appear before the OK. The 72 is an ASCII 'H' and 105 is an 'i'. EMIT takes the number on the stack and outputs it as a character. If you want to find the ASCII value for any character, you can use the word ASCII . Enter:
CHAR W . CHAR % DUP . EMIT CHAR A DUP . 32 + EMIT這本手冊背後有一個 ASCII 圖表來列出完整的字元集. (譯註: 電子版的沒有, 但是可用下列小程式列出: 256 32 do i emit bl emit i 16 mod 0 = if cr then loop )
There is an ASCII chart in the back of this manual for a complete character list.
注意, CHAR 這個詞有點不平常, 因為它並不是從堆疊輸入的, 而是將跟隨的字元 當作輸入的符號. 在堆疊示意圖中, 我們將輸入放入尖括弧中來代表這種情形 <input>. 下面是 CHAR 的堆疊示意圖.
Notice that the word CHAR is a bit unusual because its input comes not from the stack, but from the following text. In a stack diagram, we represent that by putting the input in angle brackets, <input>. Here is the stack diagram for CHAR.
CHAR ( <char> -- char , 將字元的 ASCII 值放進堆疊中 )
CHAR ( <char> -- char , get ASCII value of a character )
使用 EMIT 來顯示字串是非常冗長乏味的. 幸運地我們有一種更好的方法. 輸入:
Using EMIT to output character strings would be very tedious. Luckily there is a better way. Enter:
: TOFU ." Yummy bean curd!" ; TOFU." 詞, 發音為 "dot quote", 將會把到引號為止的所有跟隨符號顯示到螢幕上. 你需要確定在第一個引號後留一個空白. 當你需要顯示一個換行符號時, 你可以用 CR 這個詞來顯示. 輸入:
The word ." , pronounced "dot quote", will take everything up to the next quotation mark and print it to the screen. Make sure you leave a space after the first quotation mark. When you want to have text begin on a new line, you can issue a carriage return using the word CR . Enter:
: SPROUTS ." Miniature vegetables." ; : MENU CR TOFU CR SPROUTS CR ; MENU你可以用 SPACE 來顯示一個空白字元. 一串空白字元可以用 SPACES 來輸出. 輸入:
You can emit a blank space with SPACE . A number of spaces can be output with SPACES . Enter:
CR TOFU SPROUTS CR TOFU SPACE SPROUTS CR 10 SPACES TOFU CR 20 SPACES SPROUTS對於字元的輸入, Forth 可以用 KEY 這個詞, 它是輸出用詞 EMIT 的相反. KEY 將會等待使用者按一個鍵, 並且把這個鍵的值留在堆疊上. 試試下列程式:
For character input, Forth uses the word KEY which corresponds to the word EMIT for output. KEY waits for the user to press a key then leaves its value on the stack. Try the following.
: TESTKEY ( -- ) ." Hit a key: " KEY CR ." That = " . CR ; TESTKEY[注意: 在某些電腦上, 輸入會有緩衝區, 所以你必須在輸入字元後按 ENTER 鍵. ]
[Note: On some computers, the input if buffered so you will need to hit the ENTER key after typing your character.]
EMIT ( char -- , 輸出一個字元 )
KEY ( -- char , 輸入一個字元 )
SPACE ( -- , 輸出一個空白 )
SPACES ( n -- , 輸出 n 個空白 )
CHAR ( <char> -- char , 將跟隨的字元轉成 ASCII 值 )
CR ( -- , 顯示換行符號 )
." ( -- , 輸出在 " 分界前的文字 ) ------------------------------------------------------------
EMIT ( char -- , output character )
KEY ( -- char , input character )
SPACE ( -- , output a space )
SPACES ( n -- , output n spaces )
CHAR ( <char> -- char , convert to ASCII )
CR ( -- , start new line , carriage return )
." ( -- , output " delimited text )
PForth can read read from ordinary text files so you can use any editor that you wish to write your programs.
Enter into your file, the following code.
\ Sample Forth Code \ Author: your name
: SQUARE ( n -- n*n , square number ) DUP * ;
: TEST.SQUARE ( -- ) CR ." 7 squared = " 7 SQUARE . CR ;現在把這個檔案儲存到磁碟上.
Now save the file to disk.
在 \ 字元後跟隨的文字將會被當成註解. 它在 BASIC 中是 REM 敘述, 在 C 中是 /* --- */. 在括號中的文字也會被當成註解.
The text following the \ character is treated as a comment. This would be a REM statement in BASIC or a /*---*/ in 'C'. The text in parentheses is also a comment.
"INCLUDE" in Forth means to compile from a file.
你可以用 INCLUDE 指令來編譯這一個檔案. 如果你將你的檔案存成 SAMPLE.FTH, 然後你就可以用下列的指令來編譯它:
You can compile this file using the INCLUDE command. If you saved your file as WORK:SAMPLE, then compile it by entering:
INCLUDE SAMPLE.FTHForth 將會編譯你的檔案, 並且告訴你有多少位元組已經被加進詞典中了. 要測試 你寫的詞, 輸入:
Forth will compile your file and tell you how many bytes it has added to the dictionary. To test your word, enter:
TEST.SQUARE你的兩個詞, SQUARE 和 TEST.SQUARE 現在已經在 Forth 詞典中了. 我們現在可以 做一件在一個程式設計語言中非常不平常的事情. 我們可以 "反編譯" 這些程式碼, 只須告訴 Forth FORGET 它. 輸入:
Your two words, SQUARE and TEST.SQUARE are now in the Forth dictionary. We can now do something that is very unusual in a programming language. We can "uncompile" the code by telling Forth to FORGET it. Enter:
FORGET SQUARE這將會從詞典中移除 SQUARE 和任何緊隨著它的任何事物, 例如: TEST.SQUARE. 如果你現在試著執行 TEST.SQUARE, 它將不會被找到.
This removes SQUARE and everything that follows it, ie. TEST.SQUARE, from the dictionary. If you now try to execute TEST.SQUARE it won't be found.
現在讓我對我們的檔案做一些修改, 再重新載入它. 回到編輯器, 並且做出下列的 改變: (1) 改變 TEST.SQUARE 成使用 15 而不是 7, (2) 在 SQUARE 的定義之前加入 下列這一行:
Now let's make some changes to our file and reload it. Go back into the editor and make the following changes: (1) Change TEST.SQUARE to use 15 instead of 7 then (2) Add this line right before the definition of SQUARE:
ANEW TASK-SAMPLE.FTH現在儲存你的修改, 並且回到 Forth 視窗.
Now Save your changes and go back to the Forth window.
你也許會好奇以 ANEW 開始的這一行的作用是什麼? ANEW 總是用在一個 檔案的開頭. 它在字典中的程式碼之前定義了一個特別的記號. 這個記號通常有 一個字首是 "TASK-", 並且跟隨著這個檔案的名字. 當你重新 INCLUDE 這一個檔案 時, ANEW 將會自動地 FORGET 從上面 ANEW 敘述開始的所有程式碼. 這允許你一次 又一次地重新 INCLUDE 一個檔案, 而不需手動地 FORGET 這個檔案的第一個詞. 如果沒有 FORGET 這些程式碼, 詞典最終將會被填滿.
You're probably wondering what the line starting with ANEW was for. ANEW is always used at the beginning of a file. It defines a special marker word in the dictionary before the code. The word typically has "TASK-" as a prefix followed by the name of the file. When you ReInclude a file, ANEW will automatically FORGET the old code starting after the ANEW statement. This allows you to Include a file over and over again without having to manually FORGET the first word. If the code was not forgotten, the dictionary would eventually fill up.
如果你有一個大計劃需要一堆檔案, 你可以用一個檔案來自動載入所有你需要 的檔案. 有時你需要載入有可能已載人的程式碼. INCLUDE? 詞將會只載入 不在詞典中的程式碼. 在接下來的例子中, 我假定檔案是在磁碟機 WORK: 並且叫做 SAMPLE. 如果不是, 請用實際的名字取代. 輸入:
If you have a big project that needs lots of files, you can have a file that will load all the files you need. Sometimes you need some code to be loaded that may already be loaded. The word INCLUDE? will only load code if it isn't already in the dictionary. In this next example, I assume the file is on the volume WORK: and called SAMPLE. If not, please substitute the actual name. Enter:
FORGET TASK-SAMPLE.FTH INCLUDE? SQUARE WORK:SAMPLE INCLUDE? SQUARE WORK:SAMPLE只有第一個 INCLUDE? 將會導致這一個檔案被載入.
Only the first INCLUDE? will result in the file being loaded.
Forth does not rely as heavily on the use of variables as other compiled languages. This is because values normally reside on the stack. There are situations, of course, where variables are required. To create a variable, use the word VARIABLE as follows:
VARIABLE MY-VAR這會創造一個命令為 MY-VAR 的詞. 在記憶體中的一塊空間將會保留為儲存 32-bit 的數值之用. VARIABLE 被當成 "創造詞", 因為它將會在詞典中創造新的詞. 現在 輸入:
This created a variable named MY-VAR . A space in memory is now reserved to hold its 32-bit value. The word VARIABLE is what's known as a "defining word" since it creates new words in the dictionary. Now enter:
MY-VAR .你看到的這個數字是保留給 MY-VAR 記憶體中的位置. 要將資料存進記憶體中, 你可 以使用 ! 詞, 發音是 "store". 它看起來是驚嘆號, 但是對於一個 Forth 程式設計師來說, 它是將 32-bit 的資料寫進記憶體中的方法. 要讀取包含在這 記憶體位置中的數值時, 使用 Forth 詞 @, 發音為 "fetch". 試著輸入 下列程式:
The number you see is the address, or location, of the memory that was reserved for MY-VAR. To store data into memory you use the word ! , pronounced "store". It looks like an exclamation point, but to a Forth programmer it is the way to write 32-bit data to memory. To read the value contained in memory at a given address, use the Forth word @ , pronounced "fetch". Try entering the following:
513 MY-VAR ! MY-VAR @ .這將會設定變數 MY-VAR 的值為 513, 然後讀回這個數值並且印出它. 這個堆疊示意圖 如下所述:
This sets the variable MY-VAR to 513 , then reads the value back and prints it. The stack diagrams for these words follows:
@ ( address -- value , 將值從記憶體位置中取出 )
! ( value address -- , 將值存進記憶體位置 )
VARIABLE ( <name> -- , 宣告一個 4 位元組的記憶體空間 )
@ ( address -- value , FETCH value FROM address in memory )
! ( value address -- , STORE value TO address in memory )
VARIABLE ( <name> -- , define a 4 byte memory storage location)
一個用來檢查變數數值的方便的詞是 ? , 發音為 "question". 試著輸入:
A handy word for checking the value of a variable is ? , pronounced "question". Try entering:
MY-VAR ?如果 ? 沒有被定義, 我們可以定義它為:
If ? wasn't defined, we could define it as:
: ? ( address -- , look at variable ) @ . ;想像你正在寫一個遊戲, 並且你要追蹤最高的分數. 你可以將最高分保存在一個 變數之中. 當你回報一個分數時, 你可以再檢查一次最高分數. 試著以上一節所述 的方式輸入下列程式碼:
Imagine you are writing a game and you want to keep track of the highest score. You could keep the highest score in a variable. When you reported a new score, you could check it aginst the highest score. Try entering this code in a file as described in the previous section:
VARIABLE HIGH-SCORE
: REPORT.SCORE ( score -- , print out score ) DUP CR ." Your Score = " . CR HIGH-SCORE @ MAX ( calculate new high ) DUP ." Highest Score = " . CR HIGH-SCORE ! ( update variable ) ;將這個檔案存到磁碟中, 並且用 INCLUDE 詞來編譯這個程式碼. 以下列的方式 測試你的詞:
Save the file to disk, then compile this code using the INCLUDE word. Test your word as follows:
123 REPORT.SCORE 9845 REPORT.SCORE 534 REPORT.SCOREForth 詞 @ 和 ! 以 32-bit 的單位來運作. 有些 Forth 是 "16-bit" Forth. 它們 以 16-bit 的單位來存取. Forth 有些詞是用來在 8-bit 和 16-bit 的單位下運作的. C@ 和 C! 是以字元為單位來運作的, 它們通常是 8-bit 的單位. 'C' 代表字元, 因為 ASCII 字元是 8-bit 的數值. 使用 W@ 和 W! 來處理 16-bit 單位的資料.
The Forth words @ and ! work on 32-bit quantities. Some Forths are "16-bit" Forths. They fetch and store 16-bit quantities. Forth has some words that will work on 8 and 16-bit values. C@ and C! work characters which are usually for 8-bit bytes. The 'C' stands for "Character" since ASCII characters are 8-bit numbers. Use W@ and W! for 16-bit "Words."
另外的有用詞是 +! , 發音成 "plus store". 它將一個數值加進一個 記憶體中的 32-bit 數值. 試試:
Another useful word is +! , pronounced "plus store." It adds a value to a 32-bit value in memory. Try:
20 MY-VAR ! 5 MY-VAR +! MY-VAR @ .Forth 也提供一些跟 VARIABLE 很像的詞. 在詞彙表中找出 VALUE 和 ARRAY. 也看看 區域變數(local variables)" 這一節, 它是只存在 Forth 詞執行時的變數.
Forth also provides some other words that are similar to VARIABLE. Look in the glossary for VALUE and ARRAY. Also look at the section on "local variables" which are variables which only exist on the stack while a Forth word is executing.
警告有關於提取和存入資料至記憶體的一段話: 你目前學會的 Forth 指令是危險的. 一個電腦的運作是奠基於在記憶體的正確位置 有正確的數值. 你現在知道了如何在記憶體的任何地方寫入新數值. 由於一個位置 也只是一個數值, 你可以, 但不應該輸入以下的程式:
A word of warning about fetching and storing to memory: You have now learned enough about Forth to be dangerous. The operation of a computer is based on having the right numbers in the right place in memory. You now know how to write new numbers to any place in memory. Since an address is just a number, you could, but shouldn't, enter:
73 253000 ! ( Do NOT do this. )253000 將會被當成一個位置, 而你將會把這位置的值設成 73. 我不知道在這之後 會發生什麼, 也許什麼事都沒有. 這就像用來福槍射穿你公寓建築的牆. 你不知道 你將會射到誰或什麼東西. 由於你與其他的程式和作業系統分享程式碼, 你將很容易 導致電腦表現得很奇怪, 甚至 crash. 然而不要讓這件事困擾你太久. 電腦 crash 並 不像是汽車 crash 一樣, 不會傷害電腦. 你只須要重開機. 最嚴重的事有可能發生 在你正在寫入資料到磁碟時, 電腦 crash 了, 你可能損失一個檔案. 這就是為什麼 我們製作備分的原因. 這相同的潛在的問題存在於任何強力的語言, 並不是只有 Forth. 然而這有可能較不會出現於 BASIC 中, 因為 BASIC 為你保護了一大堆事情, 包括 寫一個強力程式的危險.
The 253000 would be treated as an address and you would set that memory location to 73. I have no idea what will happen after that, maybe nothing. This would be like firing a rifle through the walls of your apartment building. You don't know who or what you are going to hit. Since you share memory with other programs including the operating system, you could easily cause the computer to behave strangely, even crash. Don't let this bother you too much, however. Crashing a computer, unlike crashing a car, does not hurt the computer. You just have to reboot. The worst that could happen is that if you crash while the computer is writing to a disk, you could lose a file. That's why we make backups. This same potential problem exists in any powerful language, not just Forth. This might be less likely in BASIC, however, because BASIC protects you from a lot of things, including the danger of writing powerful programs.
另一種遇到問題的方式是一種叫做 "奇數位置記憶體存取". 68000 處理器將詞 和長詞排列為 16 和 32 bit 的數字, 在偶數的位置. 如果你在奇數的位置上做 @、!、W@ 或 W! 的動作, 68000 處理器將會因此 而產生一個例外情況, 並且試著去結束.
Another way to get into trouble is to do what's called an "odd address memory access." The 68000 processor arranges words and longwords, 16 and 32 bit numbers, on even addresses. If you do a @ or ! , or W@ or W! , to an odd address, the 68000 processor will take exception to this and try to abort.
Forth 利用 trap 這種例外並且回到 OK 提示符號的方式來提供一些保護. 如果 你真的需要存取在奇數位置上的資料, 檢查在詞典中的 ODD@ 和 ODD! 指令. C@ 和 C! 在奇數及偶數位置上都工作得很好.
Forth gives you some protection from this by trapping this exception and returning you to the OK prompt. If you really need to access data on an odd address, check out the words ODD@ and ODD! in the glossary. C@ and C! work fine on both odd and even addresses.
If you have a number that is appearing often in your program, we recommend that you define it as a "constant." Enter:
128 CONSTANT MAX_CHARS MAX_CHARS .我們剛定義了一個詞叫做 MAX_CHARS, 使用它時, 它將會傳回它被定義時的堆疊上的值. 它不能被改變, 除非你編輯程式並且重新編譯它. 使用 CONSTANT 可以增加 你的程式的可讀性並且減少錯誤. 想像如果你常在你的程式引用到 128 這個數字, 例如 8 次. 然後你決定將這個數字改變成 256. 如果你廣域地改變所有的 128 成 256, 你也許會修改到不打算改變的部分. 如果你手動修改, 你可能會遺漏部分, 特別是在 你的程式由超過一個檔案組成的情況. 使用 CONSTANT 將會讓這種修改更容易. 而使用的程式碼會跟直接使用數字一樣快並且佔用一樣少的空間. 我建議對於所有 的數字都將它定義成常數.
We just defined a word called MAX_CHARS that returns the value on the stack when it was defined. It cannot be changed unless you edit the program and recompile. Using CONSTANT can improve the readability of your programs and reduce some bugs. Imagine if you refer to the number 128 very often in your program, say 8 times. Then you decide to change this number to 256. If you globally change 128 to 256 you might change something you didn't intend to. If you change it by hand you might miss one, especially if your program occupies more than one file. Using CONSTANT will make it easy to change. The code that results is equally as fast and small as putting the numbers in directly. I recommend defining a constant for almost any number.
These next two sections are concerned with decision making. This first section deals with answering questions like "Is this value too large?" or "Does the guess match the answer?". The answers to questions like these are either TRUE or FALSE. Forth uses a 0 to represent FALSE and a -1 to represent TRUE. TRUE and FALSE have been capitalized because they have been defined as Forth constants. Try entering:
23 71 = . 18 18 = .你將會注意到第一行顯示 0, 就是 FALSE. 而第二行是 -1, 就是 TRUE. 等號在 Forth 中是被當成一個問題, 而不是敘述. 它會問是否堆疊上的兩個數值是相等的. 它將 不會將它們兩者設成相等. 你可以問其他的問題. 輸入:
You will notice that the first line printed a 0, or FALSE, and the second line a -1, or TRUE. The equal sign in Forth is used as a question, not a statement. It asks whether the top two items on the stack are equal. It does not set them equal. There are other questions that you can ask. Enter:
23 198 < . 23 198 > . 254 15 > .在加洲, 可以喝酒的年齡是 21. 你現在可以寫一個簡單的詞來幫助酒保. 輸入:
In California, the drinking age for alcohol is 21. You could write a simple word now to help bartenders. Enter:
: DRINK? ( age -- flag , can this person drink? ) 20 > ;
20 DRINK? . 21 DRINK? . 43 DRINK? .在堆疊示意圖中的 flag 一詞是代表一個邏輯值.
The word FLAG in the stack diagram above refers to a logical value.
Forth 提供了特別的詞來使一個數值與 0 相比. 它們是 0=, 0> 和 0< . 使用 0> 會比分開地呼叫 0 和 > 更快. 輸入:
Forth provides special words for comparing a number to 0. They are 0= 0> and 0< . Using 0> is faster than calling 0 and > separately. Enter:
For more complex decisions, you can use the Boolean operators OR , AND , and NOT . OR returns a TRUE if either one or both of the top two stack items are true.
TRUE TRUE OR . TRUE FALSE OR . FALSE FALSE OR .AND 只有在兩者都是 TRUE 時才傳回 TRUE.
AND only returns a TRUE if both of them are true.
TRUE TRUE AND . TRUE FALSE AND .NOT 會顛倒堆疊上旗標的數值. 輸入:
NOT reverses the value of the flag on the stack. Enter:
TRUE . TRUE NOT .邏輯運算可以合併.
Logical operators can be combined.
56 3 > 56 123 < AND . 23 45 = 23 23 = OR .這裡是這類詞的堆疊示意圖. 觀看詞典來得到更完整的列表.
Here are stack diagrams for some of these words. See the glossary for a more complete list.
< ( a b -- flag , 如果 A 小於 B, 則真 )
> ( a b -- flag , 如果 A 大於 B, 則真 )
= ( a b -- flag , 如果 A 等於 B, 則真 )
0= ( a -- flag , 如果 A 等於 0, 則真 )
OR ( a b -- a||b , 對於 A 和 B 中的位元進行 OR 運算 )
AND ( a b -- a&b , 對於 A 和 B 中的位元進行 AND 運算 )
NOT ( flag -- opposite-flag , 若真則假, 若假則真 ) ---------------------------------------------------------
< ( a b -- flag , flag is true if A is less than B )
> ( a b -- flag , flag is true if A is greater than B )
= ( a b -- flag , flag is true if A is equal to B )
0= ( a -- flag , true if a equals zero )
OR ( a b -- a||b , perform logical OR of bits in A and B )
AND ( a b -- a&b , perform logical AND of bits in A and B )
NOT ( flag -- opposite-flag , true if false, false if true )
1) Write a word called LOWERCASE? that returns TRUE if the number on top of the stack is an ASCII lowercase character. An ASCII 'a' is 97 . An ASCII 'z' is 122 . Test using the characters " A ` a q z { ".
CHAR A LOWERCASE? . ( should print 0 ) CHAR a LOWERCASE? . ( should print -1 )問題的答案 可以在這份教學文件的結尾找到.
You will now use the TRUE and FALSE flags you learned to generate in the last section. The "flow of control" words accept flags from the stack, and then possibly "branch" depending on the value. Enter the following code.
: .L ( flag -- , print logical value ) IF ." True value on stack!" ELSE ." False value on stack!" THEN ;
0 .L FALSE .L TRUE .L 23 7 < .L你可以看見當一個 TRUE 在堆疊上時, 第一部分被執行. 如果一個 FALSE 在堆疊上時, 第一個部分被跳過, 而第二部分被執行. 如果輸入下列程式碼, 你可以發現一件有趣的事情.
You can see that when a TRUE was on the stack, the first part got executed. If a FALSE was on the stack, then the first part was skipped, and the second part was executed. One thing you will find interesting is that if you enter:
23 .L在堆疊上的值將會被當成 TRUE. 流程控制詞將會把非零的值當成 TRUE.
the value on the stack will be treated as true. The flow of control words consider any value that does not equal zero to be TRUE.
在 IF...THEN 架構中, ELSE 是選用的. 試試下列程式碼:
The ELSE word is optional in the IF...THEN construct. Try the following:
: BIGBUCKS? ( ammount -- ) 1000 > IF ." That's TOO expensive!" THEN ;
531 BIGBUCKS? 1021 BIGBUCKS?許多 Forth 也支援很像 C 語言中 switch() 的 CASE 敘述. 輸入:
Many Forths also support a CASE statement similar to switch() in 'C'. Enter:
: TESTCASE ( N -- , respond appropriately ) CASE 0 OF ." Just a zero!" ENDOF 1 OF ." All is ONE!" ENDOF 2 OF WORDS ENDOF DUP . ." Invalid Input!" ENDCASE CR ;
0 TESTCASE 1 TESTCASE 5 TESTCASE閱讀詞典中的 CASE 來取得更多資訊.
See CASE in the glossary for more information.
1) Write a word called DEDUCT that subtracts a value from a variable containing your checking account balance. Assume the balance is in dollars. Print the balance. Print a warning if the balance is negative.
VARIABLE ACCOUNT
: DEDUCT ( n -- , subtract N from balance ) ????????????????????????????????? ( you fill this in ) ;
300 ACCOUNT ! ( initial funds ) 40 DEDUCT ( prints 260 ) 200 DEDUCT ( print 60 ) 100 DEDUCT ( print -40 and give warning! )問題的答案 可以在這份教學文件的結尾找到.
Another useful pair of words is BEGIN...UNTIL . These are used to loop until a given condition is true. Try this:
: COUNTDOWN ( N -- ) BEGIN DUP . CR ( 顯示在堆疊最上層的數值 ) 1- DUP 0< ( LOOP 直到我們小於零 ) UNTIL ;
16 COUNTDOWN
: COUNTDOWN ( N -- ) BEGIN DUP . CR ( print number on top of stack ) 1- DUP 0< ( loop until we go negative ) UNTIL ;
16 COUNTDOWN這個詞將會從 N 計數到 0.
This word will count down from N to zero.
如果你知道你將執行一個迴圈多少次, 你可以用 DO...LOOP 的架構, 輸入:
If you know how many times you want a loop to execute, you can use the DO...LOOP construct. Enter:
: SPELL ." ba" 4 0 DO ." na" LOOP ;這將會印出 "ba" 並且跟隨著四次的 "na". 結束的值是比開始的值先放在堆疊上的. 小心不要放反了這兩個值. Forth 將會開始它的長路, 會花不少時間. 這個順序的 理由是更容易利用堆疊傳遞迴圈的計數值. 研究一下作字元圖形的下列詞, 輸入:
This will print "ba" followed by four occurrences of "na". The ending value is placed on the stack before the beginning value. Be careful that you don't pass the values in reverse. Forth will go "the long way around" which could take awhile. The reason for this order is to make it easier to pass the loop count into a word on the stack. Consider the following word for doing character graphics. Enter:
: PLOT# ( n -- ) 0 DO [CHAR] - EMIT LOOP CR ;
CR 9 PLOT# 37 PLOT#如果你要取得迴圈計數值, 你可以使用這個詞 'I'. 這裡有一個簡單的詞可以顯示 數字和它們相關的 ASCII 字元.
If you want to access the loop counter you can use the word I . Here is a simple word that dumps numbers and their associated ASCII characters.
: .ASCII ( end start -- , dump characters ) DO CR I . I EMIT LOOP CR ;
80 64 .ASCII如果你要在迴圈結束之前離開一個 DO LOOP 迴圈, 你可以使用詞 LEAVE. 輸入:
If you want to leave a DO LOOP before it finishes, you can use the word LEAVE. Enter:
: TEST.LEAVE ( -- , 顯示 leave 如何使用 ) 100 0 DO I . CR \ 印出 loop 的索引值 I 20 > \ I 超過了 20 嗎? IF LEAVE THEN LOOP ; TEST.LEAVE \ 將會印出 0 到 21 (因為 21 顯示之後才跳出)
: TEST.LEAVE ( -- , show use of leave ) 100 0 DO I . CR \ print loop index I 20 > \ is I over 20 IF LEAVE THEN LOOP ; TEST.LEAVE \ will print 0 to 20請查閱手冊來學習如何使用下列的詞: +LOOP 和 RETURN. FIXME
Please consult the manual to learn about the following words +LOOP and RETURN . FIXME
另一種有用的迴圈架構是 BEGIN WHILE REPEAT 迴圈. 這允許你在實際 在每次做任何事情之前, 先做迴圈的測試. 如果堆疊上的旗標值是 TRUE 的話, WHILE 詞將會繼續讓迴圈執行. 輸入:
Another useful looping construct is the BEGIN WHILE REPEAT loop. This allows you to make a test each time through the loop before you actually do something. The word WHILE will continue looping if the flag on the stack is True. Enter:
: SUM.OF.N ( N -- SUM[N] , 計算 N 個整數累加之值 ) 0 \ SUM 的起始值 BEGIN OVER 0> \ N 大於零? WHILE OVER + \ 將 N 加進總和之中 SWAP 1- SWAP \ N 減一 REPEAT SWAP DROP \ 丟掉 N ;
4 SUM.OF.N \ 印出 10 ( 1+2+3+4 )
: SUM.OF.N ( N -- SUM[N] , calculate sum of N integers ) 0 \ starting value of SUM BEGIN OVER 0> \ Is N greater than zero? WHILE OVER + \ add N to sum SWAP 1- SWAP \ decrement N REPEAT SWAP DROP \ get rid on N ;
4 SUM.OF.N \ prints 10 ( 1+2+3+4 )
2) 用 BEGIN UNTIL 來改寫 SUM.OF.N
3) 加分題, 不要用任何迴圈或是條件式的方式來改寫 SUM.OF.N
1) Rewrite SUM.OF.N using a DO LOOP.
2) Rewrite SUM.OF.N using BEGIN UNTIL.
3) For bonus points, write SUM.OF.N without using any looping or conditional construct!
問題的答案 可以在這份教學文件的結尾找到.
Answers to the problems can be found at the end of this tutorial.
You learned earlier how to do single character I/O. This section concentrates on using strings of characters. You can embed a text string in your program using S". Note that you must follow the S" by one space. The text string is terminated by an ending " .Enter:
: TEST S" Hello world!" ; TEST .S注意 TEST 將會留兩個數字在堆疊上. 第一個數字是第一個字元的位置. 第二個數字 是這個字串中字元的數量. 你可以用下列的方式來印出這個字串中的字元.
Note that TEST leaves two numbers on the stack. The first number is the address of the first character. The second number is the number of characters in the string. You can print the characters of the string as follows.
TEST DROP \ 丟棄字元的數目 DUP C@ EMIT \ 印出第一個字元 'H' CHAR+ DUP C@ EMIT \ 印出第二個字元 'e' \ 等等
TEST DROP \ get rid of number of characters DUP C@ EMIT \ prints first character, 'H' CHAR+ DUP C@ EMIT \ prints second character, 'e' \ and so onCHAR+ 將位置增加至指向下一個字元. 你可以用 TYPE 指令印出整個字串.
CHAR+ advances the address to the next character. You can print the entire string using TYPE.
TEST TYPE TEST 2/ TYPE \ 印出字串的一半
TEST TYPE TEST 2/ TYPE \ print half of string如果我們只需用一個位置來描述字串, 而不需一起傳遞字串的長度的話, 將會很方便. C 語言的這種做法是在字串的結尾放置一個零來表示它結束. Forth 的解決方法不同. 一個 Forth 的文字字串是在第一個位元組有字串長度的計數, 而緊隨著就是字串中 字元的內容. 這種格式的字串可以 Forth 的詞 C" 來製造出來, 發音成 "c quote". 輸入:
It would be nice if we could simply use a single address to describe a string and not have to pass the number of characters around. 'C' does this by putting a zero at the end of the string to show when it ends. Forth has a different solution. A text string in Forth consists of a character count in the first byte, followed immediately by the characters themselves. This type of character string can be created using the Forth word C" , pronounced 'c quote'. Enter:
: T2 C" Greetings Fred" ; T2 .印出來的數字應該是字串開始的位置. 它應該有一個位元組包含字元的長度. 現在輸入:
The number that was printed was the address of the start of the string. It should be a byte that contains the number of characters. Now enter:
T2 C@ .你應該看到系統印出 14. 記住 C@ 會從堆疊上的位置取得一個字元/位元組. 你可以利用 COUNT 指令把一個有計數長度的 Forth 字串轉換成一個位置.
You should see a 14 printed. Remember that C@ fetches one character/byte at the address on the stack. You can convert a counted Forth string to an address and count using COUNT.
T2 COUNT .S TYPE使用 COUNT 將會得到字串的長度和它的開始位置. COUNT 只適用於長度小於 256 的字元集合, 因為 255 是可以存入計數位元組的最大長度. 然而 TYPE 可以適用 於更大長度的字串, 因為它使用的長度是放置在堆疊上的. 它們的堆疊示意圖如下所示:
The word COUNT extracts the number of characters and their starting address. COUNT will only work with strings of less than 256 characters, since 255 is the largest number that can be stored in the count byte. TYPE will, however, work with longer strings since the length is on the stack. Their stack diagrams follow:
CHAR+ ( address -- address' , 將位置增加一個字元的大小 )
COUNT ( $addr -- addr #bytes , 分解字串資訊 )
TYPE ( addr #bytes -- , 輸出在位置中的若干字元 )
CHAR+ ( address -- address' , add the size of one character )
COUNT ( $addr -- addr #bytes , extract string information )
TYPE ( addr #bytes -- , output characters at addr )
$addr 是計數位元組的位置. 錢字符號是用來標明跟字串相關的詞.
The $addr is the address of a count byte. The dollar sign is often used to mark words that relate to strings.
你可以輕易地使用 ACCEPT 詞來輸入一個字串. (你也許要把上面的例子 放進一個檔案中, 因為它們非常方便.) ACCEPT 從鍵盤中輸入字元, 並且將 它放進指定的位置. ACCEPT 輸入時是接收到最大接收量或是收到換行符號為止. ACCEPT 會傳回輸入了多少字元. 你可以寫一個詞來輸入文字. 輸入:
You can easily input a string using the word ACCEPT. (You may want to put these upcoming examples in a file since they are very handy.) The word ACCEPT receives characters from the keyboard and places them at any specified address. ACCEPT takes input characters until a maximum is reached or an end of line character is entered. ACCEPT returns the number of characters entered. You can write a word for entering text. Enter:
: INPUT$ ( -- $addr ) PAD 1+ ( 留一位元組的計數空間 ) 127 ACCEPT ( 接受最多 127 字元 ) PAD C! ( 設定計數位元組 ) PAD ( 傳回字串的位置 ) ;
INPUT$ COUNT TYPE
: INPUT$ ( -- $addr ) PAD 1+ ( leave room for byte count ) 127 ACCEPT ( recieve a maximum of 127 chars ) PAD C! ( set byte count ) PAD ( return address of string ) ;
INPUT$ COUNT TYPE輸入一個字串, 它將會被顯示出來. 你可以用這個程式來寫格式化的信.
Enter a string which should then be echoed. You could use this in a program that writes form letters.
: FORM.LETTER ( -- ) ." Enter customer's name." CR INPUT$ CR ." Dear " DUP COUNT TYPE CR ." Your cup that says " COUNT TYPE ." is in the mail!" CR ;ACCEPT ( addr maxbytes -- numbytes , 輸入文字, 儲存至位置)
你可以用剛寫的詞 INPUT$ 來寫一個從鍵盤中讀取數字的程式.
You can use your word INPUT$ to write a word that will read a number from the keyboard. Enter:
: INPUT# ( -- N true | false ) INPUT$ ( 取得字串 ) NUMBER? ( 如果字串是數字則加以轉換 ) IF DROP TRUE ( 丟棄高單元 ) ELSE FALSE THEN ;
: INPUT# ( -- N true | false ) INPUT$ ( get string ) NUMBER? ( convert to a string if valid ) IF DROP TRUE ( get rid of high cell ) ELSE FALSE THEN ;這個詞將會傳回一個單精度的數字和一個 TRUE, 否則它只是傳回 FALSE. 如果輸入的字串包含一個正確的數值, NUMBER? 將會傳回一個雙精確度的數值. 雙精確度是一個 64-bit 的數值, 所以我們用 DROP 來丟棄高位元的 32 bits, 因而 得到一個單精確度的 32-bit 數字.
This word will return a single-precision number and a TRUE, or it will just return FALSE. The word NUMBER? returns a double precision number if the input string contains a valid number. Double precision numbers are 64-bit so we DROP the top 32 bits to get a single-precision 32 bit number.
Our numbering system is decimal, or "base 10." This means that a number like 527 is equal to (5*100 + 2*10 + 7*1). The use of 10 for the numeric base is a completely arbitrary decision. It no doubt has something to do with the fact that most people have 10 fingers (including thumbs). The Babylonians used base 60, which is where we got saddled with the concept of 60 minutes in an hour. Computer hardware uses base 2, or "binary". A computer number like 1101 is equal to (1*8 + 1*4 + 0*2 + 1*1). If you add these up, you get 8+4+1=13 . A 10 in binary is (1*2 + 0*1), or 2. Likewise 10 in any base N is N .
因為 Forth 可以在任何基底下工作, 所以使得探索不同的數字基底非常容易. 試試下列的指令.
Forth makes it very easy to explore different numeric bases because it can work in any base. Try entering the following:
DECIMAL 6 BINARY . 1 1 + . 1101 DECIMAL .另一個有用的數值基底是 hexadecimal, 是基底 16. 對於超過基底 10 的 系統有一個問題就是我們平常的數字系統只有數字 0 到 9. 對於 16 進位的數字, 我們使用字母 A 到 F 來表示 10 到 15. 因此 16 進位 3E7 代表 (3*256 + 14*16 + 7*1). 試著輸入:
Another useful numeric base is hexadecimal. which is base 16. One problem with bases over 10 is that our normal numbering system only has digits 0 to 9. For hex numbers we use the letters A to F for the digits 10 to 15. Thus the hex number 3E7 is equal to (3*256 + 14*16 + 7*1). Try entering:
DECIMAL 12 HEX . \ print C DECIMAL 12 256 * 7 16 * + 10 + .S DUP BINARY . HEX .一個叫做 BASE 的變數是用來追蹤目前數值基底用的. HEX, DECIMAL, 和 BINARY 都是以改變這個變數的方式來工作的. 你可以將這個變數改變 成任何你所想要的數值. 試試:
A variable called BASE is used to keep track of the current numeric base. The words HEX , DECIMAL , and BINARY work by changing this variable. You can change the base to anything you want. Try:
7 BASE ! 6 1 + . BASE @ . \ surprise!你現在是在基底 7. 當你取出並印出 BASE 的數值時會顯示 10, 因為十進位的 7 在 7 進位中是 10.
You are now in base 7 . When you fetched and printed the value of BASE, it said 10 because 7, in base 7, is 10.
PForth 定義了一個詞叫做 .HEX, 它將會把一個數字以 16 進元的單位印出, 無論目前的基底是什麼.
PForth defines a word called .HEX that prints a number as hexadecimal regardless of the current base.
DECIMAL 14 .HEX你可以為任何基底定義一個像是 .HEX 的詞. 它所需要的是一種暫時將 BASE 改變成 所需的數值, 印出數字, 然後再存回的方法. 試試下列詞:
You could define a word like .HEX for any base. What is needed is a way to temporarily set the base while a number is printed, then restore it when we are through. Try the following word:
: .BIN ( N -- , 用二進位印出 N ) BASE @ ( 存放目前的 BASE ) 2 BASE ! ( 將 BASE 設成二進位 ) SWAP . ( 印出數值 ) BASE ! ( 回存 BASE ) ;
DECIMAL 22 .BIN 22 .
: .BIN ( N -- , print N in Binary ) BASE @ ( save current base ) 2 BASE ! ( set to binary ) SWAP . ( print number ) BASE ! ( restore base ) ;
DECIMAL 22 .BIN 22 .
If your answer doesn't exactly match these but it works, don't fret. In Forth, there are usually many ways to the same thing.
1) SWAP DUP 2) ROT DROP 3) ROT DUP 3 PICK 4) SWAP OVER 3 PICK 5) -ROT 2DUP
(12 * (20 - 17)) ==> 20 17 - 12 * (1 - (4 * (-18) / 6)) ==> 1 4 -18 * 6 / - (6 * 13) - (4 * 2 * 7) ==> 6 13 * 4 2 * 7 * -
: SQUARE ( N -- N*N ) DUP * ;
: DIFF.SQUARES ( A B -- A*A-B*B ) SWAP SQUARE SWAP SQUARE - ;
: AVERAGE4 ( A B C D -- [A+B+C+D]/4 ) + + + ( add'em up ) -2 ashift ( divide by four the fast way, or 4 / ) ;
: LOWERCASE? ( CHAR -- FLAG , true if lowercase ) DUP 123 < SWAP 96 > AND ;
: DEDUCT ( n -- , subtract from account ) ACCOUNT @ ( -- n acc SWAP - DUP ACCOUNT ! ( -- acc' , update variable ) ." Balance = $" DUP . CR ( -- acc' ) 0< ( are we broke? ) IF ." Warning!! Your account is overdrawn!" CR THEN ;
: SUM.OF.N.1 ( N -- SUM[N] ) 0 SWAP \ starting value of SUM 1+ 0 \ set indices for DO LOOP ?DO \ safer than DO if N=0 I + LOOP ;
: SUM.OF.N.2 ( N -- SUM[N] ) 0 \ starting value of SUM BEGIN ( -- N' SUM ) OVER + SWAP 1- SWAP OVER 0< UNTIL SWAP DROP ;
: SUM.OF.N.3 ( NUM -- SUM[N] , Gauss' method ) DUP 1+ \ SUM(N) = N*(N+1)/2 * 2/ ;回到 pForth 首頁