2026年9月2日 星期三


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 September 2026


本文原為 2009-07-05 曾貼出過的文章,為留家譜而重貼。

又到了每年該報稅的時間,心頭上總有『大事要做,閒不得也』的感覺,十幾年來,此事均不假手他人,親自辦理,也從未出過問題,每年總有一筆稅金要繳,有所得就該有所稅,循規蹈矩,按時繳清稅款,做個守法的人,日子過得坦坦蕩蕩,就會有舒適的人生。今年報稅截止日期是七月七日蘆溝橋事變紀念日,習慣上我總是提前幾天完成,一口氣把家中所有的稅表填完、遞出,按理應該是熟能生巧,耗時不多的事情,可是每年我總是要耗掉一整天的時間才能完成,原因無它,搞稅的人每年都在填表方式上搞一點點的小改變,如此一來就可以有很多人跟著有飯吃,凡扯到報稅表的都有份,因為表就是不一樣,每年第一次填表時都得從頭到尾詳填一遍,才不會出差錯。

除此之外,西方國家對數字的處理思維,是與我們中國人完全不同的,填稅表的進行方式也就設計成只適合他們的思考模式了。本來是一個總收入減掉總支出再乘上稅率就得到答案的簡單事情,他們認為將其複雜化後,強迫填表人不得不逐步進行、逐欄填寫,才不會出差錯,我也只好跟著被表格耍著走,被迫讀完整本報稅手冊。

雖然這裡商場交易常用刷卡方式,但現金交易仍處處可見,生活久了就能深深體會西方世界腦袋瓜子是如何進行四則運算的。東西方的加法運算可能都一樣,減法就完全不同,買 35 給 100 要找 65 ,中國人是直接算出來的,這裡的店員找錢時,就從 35 起算,先拿一堆 1 元,以一次 1 元的方式往上算或想像成往上加, 36、37、38…加到 40 暫停,再改拿 10 或 20 亦或 50 的紙鈔,繼續往上加,直到加成 100 ,就完成了找錢工作,此時,你若將找得之錢先放進了口袋,回頭再問店員到底應該找我多少?對方一定大發雷霆,認為你在找麻煩,因為他確信已經用 100 跟你換完 100 了,你還想怎樣!找錢時你最好不要講話,以免打斷對方唯一只會的加法計數,否則當對方沒把握而得重新來過,係因受你打攪時,一定火大,要不然他就等你講完所有的話後才找零錢,如果你一直不停的講話,就不能怪對方一直不肯找錢了。至於乘法,據內人在小學協助教學期間所知,小學老師有時也都搞不清楚個位數乘個位數的結果,也就是普遍性的缺乏九九乘法表的記憶,我們就不用探討整個社會的乘法能力了,除法更是免談。信用卡不用算帳,要不然就等找到計算器後再說,這就是西方世界的商場交易情況。

我們一生接觸的數字,最大數目好像到『兆』這個單位也就夠了,因此很少有人去探討再往上數要用甚麼單位?最近美國搞出金融風暴後,錢不值錢了還在猛印鈔票,才感到數目字的名稱要不敷使用了,『兆』的下一個要用『京』。

其實,中、英文中都有可用來稱呼很大數字的名稱,只不過因罕用而罕有人知罷了。我幼年時讀過教育部的一本期刊,刊載了中國人的數字表示名稱,就稱之為『數譜』吧,它們的名稱由大到小依序排列如下:

極 、 載 、 正 、 澗 、 溝 、 穰 、 秭 、 垓 、 京 、 兆
億 、 萬 、 千 、 百 、 十 、 個 、 零 、 錢 、 分 、 釐
毫 、 絲 、 忽 、 微 、 纖 、 沙 、 塵 、 均 、 埃 、 渺 、 漠

極以上另有五個單位,它們是來自印度的名稱,非單個字,用名是恆河沙、阿僧祇、那由他、不可思議、無量數。『兆』以後到『極』的幾個單位可能還算是中國自己土產的,字典上查得到。後面唸起來像印度佛教用字的單位,則可確信是佛教傳入中國後跟著傳進來的東西。

最近廖瑞淦醫師借給我一本李敖的著作『北京法源寺』,據說此書曾經角逐諾貝爾獎,寫得比較中肯,我一向尊崇李敖為學歷史後的論述,但對他以前著述時的一些謾罵則不敢恭維,我自己歷史讀得不好,如要勉強引用歷史,就會岳飛打張飛,打得滿天飛,但就自己科學方面的認識,卻可以指出李敖此一名著中的錯誤,他在書中引用佛經時,以語體文解釋到『阿僧祇』是佛教中的極大數目字名稱,如果我從小的記憶沒有錯誤,從上述的數譜中可以看出,那只能算是排行老四,算不得極大或最大的名稱,『無量數』才是。李大師大概不上網,我貼網頁文章,提醒角逐諾貝爾獎的著作要謹慎一點,應該無傷於他,但我知道,如果他倒過頭來攻擊我的文章時,我一定會變得體無完膚,因此,有心人也請千萬不要在閱過此文後去挑釁李大師。

至於在英文的環境嘛,就像我的鄰居告訴我,他呀呀學語的小孫女會說兩種語言,一是 English ,另一是 Gibberish(兒語),數數目再大她也不怕, Billion、Trillion 以上她都說是 Gazzillion(很多很多的意思)。難怪用完最後一個『兆』(Trillion)這個可用單位後,世界就只好發生金融海嘯了。

這一段只是玩笑話,請別當真,事實上英文的數譜可大得多了,因為他們科技堅強,更有需要,英文的數譜我就不翻譯而直接整理如下,資料的來源是大學圖書館中好幾本科學用書的綜合收集:

我得事先聲明,此表僅供我個人工作須要時參考,可以提供給大家分享,出處太多、無從驗證,也不值得繼續吹毛求疵。 Googol 是 10 的一百次方的很大數字,它與您最近幾年慣常使用的 google 有關,知道便好。


數有數譜,許多事、物也都有譜的,我們曾家也有家譜,在家譜中,我屬『慶』字輩:

興、毓、乾、紀、廣、
昭、憲、慶、繁、祥、
令、德、維、垂、祐、
永、念、誦、顯、揚。

舉凡孔、孟、曾、顏四家子孫都用此譜排名,您若識得有這四姓的朋友,取其名中第二個字比照此譜,便能知個大概了,曾家的子孫可得牢記本譜。新聞報導,大陸上因一點芝蔴綠豆的小事,就有人發動無聊的網路人肉大搜身,真正是譁眾取寵,無聊至極。一個人如果做人處事坦坦蕩蕩,即使篇篇網頁文章都像這樣『裸奔』終身,那又如何?

最近我將自創的數學計算系統定型下來,其中包括了一個可以完全使用中文,設計出任何數值分析性程式的精簡系統,指令只須 20 幾個,測試時變數名稱若也只想用中文時,天干、地支、四維、八德的譜表整理妥當,就可以方便的使用,免得用到爛字。打字時也才發現,有幾個字從來沒把字音唸對過,搞清楚後有正音效果,它們是:

甲乙丙丁戊己庚辛壬癸
子丑寅卯辰已午未申酉戌亥
禮義廉恥
忠孝仁愛信義和平

前一陣子台灣文化遭遇過比文化大革命還糟的破壞,『三隻小豬』可以當成語,儒家思想全面被排擠。難聽的性器官,粗人講的『鼻屎』被當政者到處宣揚,比『毛語錄』還難聽。我們寫程式時最好小心一些,不要這樣,要不然 Forth 是可以隨便給字當指令的,罵起人來會比誰都惡毒,還可以設計成罵語具有深厚的內涵,認真整理別人高段的罵話,也可以押韻成一大『譜』,但不該這樣!

台灣已經不太有人再搞 Forth 了,也沒人想要這方面技術,我便請我的準女婿 Richard 引介,改在紐西蘭自求發展,參加了一個本地自組的 Linux 自由社團 WLUG(Waikato Linux Users Group),以 Forth 協助他們發展技術,第一天參加活動,感覺就很不錯,將近二十人在教堂中,一個月一次,從早上 10 點活動到下午四點,最高齡77歲,三個年輕小姐,有一名盲人朋友,除了我沒電腦外,全部自備電腦,但全部歡迎我用各個電腦展示 gForth ,友善得很,看來未來充滿了希望。現行 Linux 一開機就掛在網上,資源豐富,不輸微軟想獨霸的環境,我第一天接觸的是中文發音很難聽的『污幫吐』(Ubuntu)9.04 版系統,能取得的自由軟體相當壯觀,可惜硬碟要20G以上, RAM 也要 2G ,執行起來才算方便。我暫時還沒有方便的硬體,雖然我一直懷念、使用十幾年前的電腦,也還能創作,但要取得新時代的資源會有困難,九月回台灣時該買新品了,因為這裡有新朋友可以搭配我的發展,可以共鳴 Forth 。月底有一場以 Linux 為發展基礎,用電腦控制新型液晶電視顯示螢幕的軟體發表會,大家極力邀我參加,深信用 Forth 更易發展,也強調未來的數位型電視螢幕,給電腦很有利的發展機會,中國人不太在意此方面之事,也放棄了 Forth ,這裡有人懂,也有人想深入應用,我會前往聽講,看看能否有所建樹?

十幾年來我參加過幾個本地社團,對各種活動略有所知,也知道自己與他們格格不入的原因。先是參加了天文俱樂部,每個月在動物園門口的光學天文臺所在地活動,我去了一年,發現各個會員都是磨鏡好手,都能自製望遠鏡,而且技術精良,我這個機械工程師出身的闖入會員實在愧對大家,最後捐了一個自己庫存多年,市面也罕見的五相步進馬達,運轉起來可有最少振動,供作天文臺自動尋星定位驅動之用,結束了參與。

接著參加健行俱樂部,年輕人居多,都是健行老手帶隊活動,我先小玩了幾趟。當大家邀我參加要離家一天以上的露營健行時,我知道該收攤了,以免自己隨時可能會復發的慣性痛風,在活動期間擔誤了別人,我在台灣登過三十幾座百岳中的高山,但那是年輕時候的事情了,現在逞強不得。

丁陳老師來信告知,他七月六日要做兩條心血管手術,也算是老師傳授給我的另一種教育,要我注重身體健康,我卻只能回祝他平安愉快。

第三次參加的是航行俱樂部,很好玩,可以親自接觸在台灣都不可能接觸到的飛機,包括紐西蘭人曾經廉價自製的巡弋飛彈,但我又相形見拙太多了,玩遙控飛行器的人全能自製,資深會員都幹過戰鬥機飛行員,還有二戰期間滑翔機老教練 Stuart Rogerson 邀我同飛一趟,我不敢答應,連農場來的農民都有飛行執照,最後,我只能閉門思『弱』了。

這次在閉關多年後,重拾舊夢,養足了 Forth 精技才踏足電腦江湖,是希望還能從新時代中學些東西,我會再增加一次另種俱樂部的活動經驗。

紐西蘭人口不多,四百萬而已,南北兩島土地大約是台灣的七倍大,看起來各方面好像都不怎麼樣,它卻有一股台灣所不能及的潛力,從上述各種俱樂部的活動內容可見一般。就算科技也不落後,此地全面反核,可是您知道嗎?核子科學之父羅熱浮(Rutherford,中文教科書翻譯成拉塞福),就是南島北端內爾森(Nelson)市土生土長的紐西蘭人,羅先生最先提出『用高能粒子撞擊原子核能夠探索基本粒子』的研究模式,一直被科技界延用到現在且仍在使用;就是紐西蘭農民用槍射擊草堆(是乾牧草而不是稻草),以子彈的偏折來判斷草堆中有無鐵塊或石塊的方法。我就奇怪中國有這麼多農民,為什麼就沒人像羅先生一樣,提出此一高見?後來仔細一想,關鍵就出在我們中國農民不能擁槍自重啊!

世界各國都有自己先行的科技被糟蹋的記錄,這是由全民自己的共識來決定的,有時怪不得別人的瓢竊。 90 年代,我曾在俄羅斯數年,在許多場合聽過愛迪生發明的電燈泡是他們俄國人發明的,平日熟悉的數學定理全都冠上俄國人的名字。德國人也在飛機上免費雜誌內,大肆報導他們是首先發明造紙與印刷技術的國家,與中國的歷史記錄無關。我有種感覺,科技先進者才有民族自大的權力,我們現在只宜站得高高的,看兩馬相踢,這句話要用台語發音:『站高高,看馬仔相踢。』,那才傳神,但別忘了自己該急起直追的努力,單只是 Forth 一種技術,可以不搞,但總不能甚麼都不搞,連國防都只想靠美國保護吧?

有時自己也該明白自己的能耐,不要太妄自尊大,忘了發展,例如:我們老是愛講火藥是中國人首先發明的,沒錯,那是黑火藥,是硝石、硫磺、黑碳磨成粉,按比例均勻混合後的純機械性產製『火藥』,與經由化學反應後產製的硝化甘油式『炸藥』徹底的不同。以後再提到會爆炸的東西時,中國人就請別再往自己臉上貼金,說自己是最早的發明人了,火藥非炸藥,而且發明這種東西實在丟臉,不提也罷。諾貝爾,諾先生死前體會到,發明這種可以製造大規模殺人武器用品所須材料有罪惡感,所以設立了一個名叫『諾貝爾獎』的『贖罪獎金』。能與不能領這個獎金的人都該為人類的和平好好想一想,尤其是不該領過獎後還作亂,無論是那一種亂都不應該,因為該獎源實在殺人過多,還在殺,還要殺!

我一直對自己想要設計的『地震早期警報系統』不曾忘懷,最近在實現這件理想方面也更加有譜了。 Linux 系統的最基本外殼(Shell)程式,就有現成的定時自動執行發送電子郵件的功能,根本不用設計,而只需直接對系統下命令,做好設定便可,搞 Forth 的人只須將程式抓取所得的信號,存成檔案便可,我轉往 gForth 上,設計此前已經在 Win32Forth 系統上實現過的所有程式,並不困難。

例行性的瀏覽『科學美國人』(Scientific American)89期今年五月號通俗科學雜誌時,發現可測量 20Hz 以下低聲頻的感測器與系統也已經存在了,而且基本理念與我一年多前提出的方法完全相同(還好我一年多前就在中華民國Forth語言協會,留下了網文記錄,現在還在)。只不過是,一個非常和平有益於全人類的構想,又再度被美國人濫用了,他們只拿這樣的系統,派兵監測北韓的核子試爆,卻對可能攸關大量人類生死之事置若罔聞,因為阿兵哥看不懂可能很有用的地震早期警報訊號,他們的任務朝向別處,我若深入研究,可能會被列入屬於想刺探軍機的嫌犯,想買探頭可能都有困難。

另外,想靠美國大兵監測別人核子試爆時,順便照顧一下有無地震的想法,是絕對不可靠的,想想看,靠牆牆還會倒,靠人人則會跑,美國大兵監看測震系統,可能比卡通影片的辛普森照顧原子爐還糟,如何靠?美國的兵只會欺侮弱者,從來沒有敢打強者的記錄,越戰、伊拉克戰爭不都是這樣嗎?不靠也罷。

『科學美國人』是本很好的通俗科學雜誌,早期台灣推動科學發展時,一些科技先進學者創辦的『科學月刊』,就曾大量引用這份『科學美國人』的文稿,對推動台灣科技發展很有助益,我也常看。上述該文隨後也有讀者投書,故意引介可以供應測量低到 0.001Hz 的探頭公司,看來是丹麥人開的公司,網頁是 www.bksv.com ,公司名稱 Bruel & Kuaer capsule ,我對這些事務關心,是只想測地震,不管核爆,網頁可以助我表白,因為誠懇的人可以先把譜擺在這裡。


附註 : 20260902 重新整理後貼出。

2026年8月16日 星期日

韓信點兵

韓信點兵


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 August 2026


此前貼出過之百例中的第 81 例展示過一個韓信點兵的精簡程式,該程式採用暴力計算方式,只按問題直接強行計算,算到全部合乎指定條件後,得到解答。

本文中的程式則將問題建立成表格來計算,執行對答方式輸入問題,以敘述方式給出答案。

如果不能寫出不同的見解,就不該寫這篇文章,以免浪費讀者的寶貴時間。因為這是一個古老的中國算學技術,是西元前 200 年(韓信死於西元前 196 年)左右的問題,距今約 2200 年了。

中國秦朝末年,楚漢相爭,結果漢高祖劉邦打敗項羽取得天下,建立了漢朝。那時,有所謂的漢初三傑:指的就是韓信、張良、蕭河。韓信是個軍事奇才,曾經擔任軍隊的總司令,被任命為大將軍,協助漢高祖劉邦打天下,但不幸最後卻因被懷疑意圖謀反,而被蕭河及呂后(呂雉)騙入宮中,處死於長樂宮鐘室。

中國人無人不知、無人不曉『韓信點兵』的問題,卻罕有人能熟記,解這個問題的詳細理論依據,或將其學通後加以活用,或用來解出其他所有相關與類似的問題,這種情況,經過了兩千多年,甚至於一直到今天,依然如此。

據說,這個問題還曾經被納入高中的數學教材,可以想像,數學老師教起來必然辛苦,學生也學得痛苦。我不記得我在高中學習不定方程式(Indeterminate equations)時,必須學這項主題,就算學過,也早就忘了教材中曾經教過我,該如何按邏輯嚴格演繹以求得解答的詳細步驟了,反而是單一種逐步計算的方法,我終生都記得。

本文就專門只談論這種永遠記得住的解題方法,它確實就是各種方法中的一個好方法,還可以用來實現一個永遠可以存在、放諸四海而皆準的程式,要完成這樣的程式,也必須按邏輯嚴格演繹,設計出來的程式,此後才能古今中外都派得上用場。

科學月刊中現代數學名家的『韓信點兵』文章內,批評只有『答案』與『計算技巧』的堆積,無法顯現『數學的妙趣』,我百分之百同意這樣的觀點。但是,我反對為了培養『數學的妙趣』,而刻意略去能夠直接了當解得問題答案的最簡單方法。試想,如果無限多個『韓信點兵』式的問題堆積在眼前,您該怎麼辦?當精簡的『演算技巧』,與絕對需要的『答案』,您都遲遲難以獲得時,請問,您那來的『數學妙趣』?因此,我改而另覓它途,解決『韓信點兵』問題。把思考轉向電腦程式的邏輯嚴格演繹,而不是純粹數學的邏輯嚴格演繹,完成一個對談式萬用程式,包括落定美觀的排版所耗時間,大約只花兩小時,但是,『它』今後可以永遠有用。

這篇文章的目的,並不是想否定所有有關『韓信點兵』問題的理論分析教學,相反的,我仍鼓勵大家能學通以『韓信點兵』為例而安排出來的理論分析教材內容,畢竟,它相關於其他的數學學問,適合作為人人都熟悉的問題範例。

至於想實際解出這個問題的答案時,我個人覺得,現今,應該用電腦程式來解,而且,要將程式設計成對談式的萬用程式,至少,讓這個問題今後都能如此方便的解得答案。原因在於,一方面電腦幾乎是當今人人生活上必用的工具,那麼,電腦程式解題的方法,只要能合理解得正確的答案,就該認同這個方法,沒有理由硬要排除它。另一方面,則是現行電腦的執行速度與友善的操作環境,已經發展得相當完善了,適合作為解決這種問題的工具。我將電腦程式設定成逐步計算到達十萬次以上時,就當作可以落定題目為無解,使用已經 20 幾年高齡的電腦,也不過耗時僅約一秒。為何不用電腦程式來解問題?我堅決反對拒教電腦解題技術,不教就違反時代趨勢。

台灣發行的『科學月刊』,算得上是一本優良的普及性科學雜誌,我也愛讀,雜誌內容曾經出現過許多次談論有關『韓信點兵』的討論文章。

來自四面八方的數學專家,與各大學的許多教授,都曾為『韓信點兵』問題,寫過精闢的解題方法分析文章。如果您對求解此一問題之純粹理論性的分析有興趣,可以在網上搜索出不下百篇的文章來,國際上將這種問題命名為『中國剩餘定理』(Chinese Remainder Theorem)。為了要完成本文,我做過這樣的工作,快速瀏覽這些文章後,只有一種感覺,實在不想、也根本無法記下所有的邏輯推演步驟,今生再度碰到『韓信點兵』問題時,我還是只會逐步計算,使用規律增量式的逐個試算,最後絕對能得到正確的答案。

我都已經 75 歲了,記那些解題時必須要用到的定理、定律,與專門只用於解這種問題時,才需要使用的特別符號,有必要嗎?今天記了,大概明天就忘了,何苦來哉?

為『韓信點兵』問題而公開發表過的那些高明分析文章,仔細讀完之後,對我而言,充其量也只能讓我感到各文章作者言之有理,對必須解決實際問題的最基本要求,則實在幫助不大,更遑論憑此而能衍生出更高深的知識或技術了。換句話說,那些文章該看,至於能不能因此而領悟出連貫性知識或技術?那就得看個人對此一問題的進一步修行意願了,結果絕對因人而異,而願意前行者少。

最原始的『韓信點兵』問題,節錄如下:

兵不知其數,三三數之剩二,五五數之剩三,七七數之剩二,最少兵數應該是多少?

問題可以改寫成現代人使用之數學符號表示的聯立方程式,列示如下:

X = 3 * Q(1) + 2
X = 5 * Q(2) + 3
X = 7 * Q(3) + 2

這是一個只有三個已知關係的數學聯立方程式,卻要求解四個未知變數X、Q(1)、Q(2)、Q(3)。因為還差一個已知數學關係式,所以使這種問題被歸納入不定方程式的範疇。解答必須為整數的隱含要求,是代表兵士不得為半個人,或 3.1416 個人。

為了本文隨後表示上的方便,此處刻意引用『科學月刊』中,數學專家所使用的更精簡表示符號,以 S(N|R) 來表示問題,它代表 S 以 N 除之餘 R ,S 代表這種所有數字的集合,上列三個式子因此而得以進一步簡化表示成:

S(3|2) , S(5|3) , S(7|2)

直接逐步演算的方法為:

(1) 先找出條件中最大除數的那一項條件,以此數作為整個演算的基底數字。
在上列原始問題中為 7
(2) 設定一個計數器,令其從 1 開始計數,然後算出第一個可能符合條件的數字。
在上例中為 7*1+2=9
(3) 用(2)中得到的數字,去核算並判斷是否亦符合其他條件。
在上例中, 9/3=3 餘 0,9/5=1 餘 4 ,結果與原已知條件均不符合
(4) 回到(2)中將計數器值加 1 ,繼續(3)的核算,直到滿足所有條件為止,便可得到一個首先能夠獲得的答案。
在上例中,當計數器為 3 時, 7*3+2=23,23/3=7 餘 2 , 23/5=4 餘 3,符合要求,因此, 23 就是一個首先獲得的答案。

故事書上說,漢高祖劉邦聽不懂韓信的兵數描述,轉問張良,張良卻回話說:『兵數無法算,不可數。』

這樣的回答不對,方法就像上述這麼簡單,算 3 次後就可以得到一個核心基本根據數字,數學專業術語稱作『特解』,接下來要形成『通解』時,答案就是特解加上任意一個正整數乘上所有已知條件中的除數,在上例中為:

23 + N * ( 3 * 5 * 7 ) = 23 + N * 105 其中 N = 0, 1, 2, .....

這就是全部求解『韓信點兵』問題的逐步計算法,我相信,任何人看過上述解題方法的敘述之後,都能終身記得,都能終身使用,我的程式也就是根據這麼簡單的方法設計而成的。

通常我們求解一個數學問題時,首先要確定問題的答案是否『存在』(Exist)?上例中求得的23,表示答案確實存在,它就可以被確定是問題的一個特別解答,簡稱『特解』(Particular solution)。接下來要確定問題的答案是否為『唯一』(Unique)?經過仔細研究後,我們發現這種問題的解答不是唯一的,其特解23加上所有除數相乘後再乘上任意一個正整數,也都是問題的答案,因此,我們必須將所有的解答以適當的數學式子表示出來,以形成這個問題全部的通盤解答,簡稱『通解』(General solution)。數學理論中解答的『存在』與『唯一』性質,對應到解答的『特解』與『通解』,這個『韓信點兵』問題的通解,僅只是由簡單的乘法來形成,沒有其他任何技巧,因此,本文只討論問題的特解,程式中也不計算出簡單的通解,特此聲明。

由此看來,下一次考試碰到這樣的問題時,別再依靠那些無法終身記得的分析式演繹算法了,我能確定大多數人,僅能暫時性的理解分析演繹法,如果背下方法,考完就忘了,那又何須浪費時間去背記?將這些用來理解與背記所需的時間,改用來思考解決其他的學習問題,可能比較有意義。通常,上述計數器只須計數幾次,答案就可以獲得,因為考題所給的除數都不會太大,經常是個位數,已知條件也不會太多,一般就只有二或三個,取一張計算紙,列個表,快速心算後在每一列後面做個記號,很快就會有結果,如果嫌計算冗長,影響求解其他問題,那麼,這一題留到最後才解。

平時呢?您也不必怕我這樣強調性的教您如此求解問題會害了您,就根據上述逐步演算法,我寫成了永遠都能使用的對談式萬用程式,列示於本文內,使用時,可以在電腦開機後,只操作滑鼠點選三、四次,就有一個我設計而命名為 MAIN 的指令可以直接使用。好好的使用,它就能幫助您一天內解出幾千幾百個『韓信點兵』問題。碰到子女問您同樣的問題時,就算您不懂韓信如何點兵,這個對談程式會協助您,給予您滿意的答案,讓全家人皆大歡喜,然後,您就有信心告訴子女,應該好好學 FORTH 程式語言, FORTH 能設計出所有的程式。

我原本全部使用中文設計出這個程式,完成之後,考慮本文可以供給全世界的人參考使用,於是又將一般程式部份改成了英文,如此一來,也能供給慣於使用其他程式語言者,參考後模仿設計。

我瀏覽過網路,搜索能解『韓信點兵』問題的實際可用程式,大部份都是用 C 或 Python 程式語言寫的,由於 C 不方便用來設計立即對談式(Real time interactive)程式,因此,那些程式都只能解單一個韓信 2200 年前提出來的最原始問題,我用心算都可以比發展那樣的程式還快速的得到答案,所以那些程式沒有價值。

時代不同了,現代電腦的記憶體用量與執行速度,在用來解決這種問題而必須設計程式時,已經不用強調要節省記憶體空間與程式執行效率的問題。相反的,為了讓大多數人看過程式之後,能夠一目了然,我將程式設計成不在乎多執行了一些步驟,要用到的變數宣告量,也不用斤斤計較到要強調共用而節省。程式可執行出合理範圍內的答案,韓信不會有億萬大軍,解放軍再多,也只能論及幾百萬,這是一個兼顧了實際情況的程式。

採用對談式程式語言(如 FORTH )來設計這種程式,比採用編譯式程式語言(如 C )要好,使用方便且具彈性。我的程式中雖然暫時只規劃了可以輸入最多十組已知條件,要變通時,只需修改這個設定,但當已知條件多於十個以上時,程式改成輸入數據預置於檔案程式內的方式會比較好,可以防止一鍵敲錯而滿盤皆輸必須重來的困擾。這個程式是透通的, FORTH 是必須使用模組化格式設計程式的語言,想改寫程式,簡單之至,這就是 FORTH 。

七十年前,我們唸小學高年級時,每天幾乎都要習作 50 題當年『武明算術』內的習題,我不曾不交作業,學習方面只是認真但不聰明,可是這些學習方式,影響了我的一生,這樣的學習過程,可能是決定了我能否快速設計出電腦程式來解決問題的關鍵因素,我的小學老師是內壢國民小學的范金櫻老師,能有現在的能力,該感謝我的老師。

今天,此處列示的這個有用程式,它不僅只是代表了我寫成一個實用的程式,而是代表了我設計出一套永遠都容易建立的可對談式數學計算系統,然後讓大家可以很方便的設計程式,來解決所有的數學計算問題。截至目前為止,全世界尚無人能比,而這個系統還要發展。系統並非完全由我個人隻身獨立發展而成,我善用組合能力,組合了許多名家的長處來創造出系統,也因此而令其性能傑出,也因此而令系統隨時都還能繼續發展。

我出身軍人世家,自己也是被國軍淘汰下來的職業軍人,俗話說:『好鐵不打釘,好漢不當兵』,這句俗話是錯的,全世界不是軍人的人,此前還沒有人有能力,像我這個出身為軍人的人,設計出這麼方便且能夠處理許多問題的 ABC FORTH 數學計算系統,而我卻曾經是個『兵』。軍中的同袍,也請不要妄自菲薄,平時就該努力充實自己,設法接受我的這個 ABC FORTH 數學計算系統。戰時,軍方需要快速且大量的軍事作業研究(Operation research)來提供制敵的先機,其中許多問題需要大量的數學計算。目前,全世界還找不到像 ABC FORTH 那麼方便的系統,可以立即執行,可以對談式操作,專門用來處理數學計算,能夠快速處理這種『韓信點兵』式的問題。善用這樣的系統,在技術上就可以勝過一個韓信總司令,我不會像韓信那樣,將秘笈祖傳起來,下列就列示一個祕笈。

接下來,我先以前述『科學月刊』中最佳的問題表示符號,列示十二個曾經被提出來當範例的『韓信點兵』典型問題,供作程式測試之用,接著列示我所設計的程式。最後,我將程式載入 ABC FORTH 系統,直接操作系統,求解這十二個問題,執行的結果當然都是正確的,全部顯示在畫面上。再操作滑鼠,選定執行結果所產生的整個畫面,令其反白、複製、再轉貼入此文,讓讀者能夠完完整整的看到整個結果。

如果您願意實際體會執行這個程式後,快速得到正確答案的感覺,所呈現出來的現象將會是:每當您輸入了 MAIN 指令後,就會有一個滿意的終結。不想再執行 MAIN 指令了,也可以改執行 CHECK 指令,去看一下程式內的資料內容,或者就輸入 SEE MAIN 兩個指令,看系統把我設計的程式編成甚麼模樣了?顯示出來的這些現象,係 FORTH 程式語言一旦執行起來之後,就會顯現作業系統的性能所致。這是 Win32Forth 系統的作者 Tom Zimmer ,在網路論壇上給全世界 FORTH 使用者的善意建議,我提供了全套應用實例,您請自己體會。

請注意問題(6)無解,程式執行後就顯示無解。
問題(1)就是歷史上韓信提出來的第一個問題,它再簡單,程式也要能解。
問題(9)的答案比較特別,是除數全乘起來以後再除以2,程式也毫不含糊的快速顯示正確的執行結果。
現任學校的老師,如果想要隨便出幾個題目來考學生,利用這個程式,隨便輸入一些組合條件,就能先行得到答案,這些條件,就能當作別人從未出過的考題來考學生。
學生只要肯學 FORTH ,就能利用這個程式來對付老師的刁難。
一天之內,您想解出成千上萬個這種問題的答案,怎麼沒有可能?
總而言之,這個程式是方便而萬用的。
我則能擔保,永遠都有能夠執行這個程式的系統存在世上。


●十二個典型的問題為:

(1) S(3|2) , S(5|3) , S(7|2)
(2) S(7|1) , S(8|2) , S(9|3)
(3) S(11|3) , S(12|2) , S(13|1)
(4) S(2|1) , S(5|2) , S(7|3) , S(9|4)
(5) S(5|0) , S(6|1)
(6) S(12|5) , S(18|13)
(7) S(12|5) , S(18|17)
(8) S(15|7) , S(21|13) , S(10|2)
(9) S(11|1) , S(13|2) , S(15|3) , S(17|4) , S(19|5)
(10) S(3|1) , S(5|2) , S(7|4)
(11) S(8|1) , S(7|1) , S(5|2)
(12) S(7|3) , S(11|5) , S(13|8)

●程式及執行結果列示如下:


 

\ 韓信點兵 Chinese Remainder Theorem
\ 作者:曾慶潭 於紐西蘭 
\ 版權所有,標示版權,便歡迎使用。
\ Copyright 2011-02-16 Ching-Tang Tseng	
\ Permission is granted by the author to use this software for any application
\ provided this copyright notice is preserved.

10 2 整數矩陣 TABLE    \ 編制表

整數變數 NC          \ 條件量Number of Conditions
整數變數 TOTAL       \ 總兵數
整數變數 COUNTS      \ 點兵次數
整數變數 MAXI        \ 編制為最多班兵之指標Max divisor Index
整數變數 RMOD        \ 點兵後之餘兵數Remainder after MOD
整數變數 RF          \ 點兵後之餘兵數比對旗號Remainder checking Flag
整數變數 I           \ 循環指標1
整數變數 J           \ 循環指標2

: 歸零 中文程式
10 FOR J = 1 TO 2
20 FOR I = 1 TO 10
30 LET TABLE ( I J ) = 0
40 NEXT I
50 NEXT J
60 LET NC = 0
70 LET TOTAL = 0
80 LET COUNTS = 0
90 LET MAXI = 0
100 END 
;

: 對談式輸入 中文程式
10 PRINT " 請輸入有幾個韓信點兵問題的已知條件  "
20 INPUTI NC
30 PRINT " 下列操作,將要求您每次以兩個分開的整數方式輸入 " ; NC ; " 組條件。 "
40 PRINT " 條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  "
50 FOR I = 1 TO NC
60 PRINT " 請輸入第 " ; I ; " 組條件 "
70 INPUTI TABLE ( I 1 ) , TABLE ( I 2 )
80 NEXT I
90 END
;

: 找出最多班兵之指標 中文程式
10 LET MAXI = 1
20 FOR I = 1 TO NC
30 IF TABLE ( I 1 ) > TABLE ( MAXI 1 ) THEN 50
40 GOTO 60
50 LET MAXI = I
60 NEXT I
70 END
;

: 點兵 中文程式
10 LET COUNTS = COUNTS + 1
20 LET RF = 0
30 LET TOTAL = TABLE ( MAXI 1 ) * COUNTS + TABLE ( MAXI 2 )
40 FOR I = 1 TO NC
50 LET RMOD = TOTAL MOD TABLE ( I 1 )
60 IF RMOD = TABLE ( I 2 ) THEN 80
70 LET RF = RF + 1
80 NEXT I
90 IF COUNTS > 100000 THEN 130
100 IF RF <> 0 THEN -10
110 PRINT " 點兵點了 " ; COUNTS ; " 次,得到的答案為 " ; TOTAL
120 GOTO 140
130 PRINT " 點兵次數超過十萬次,還點不出答案,太不合理,故此題無解! "
140 RUN CR CR
150 END
;

: MAIN 中文程式
10 執行 歸零
20 執行 對談式輸入
30 執行 找出最多班兵之指標
40 執行 點兵
50 結束
;

: CHECK BASIC
10 FOR I = 1 TO 2
20 FOR J = 1 TO 10
30 PRINT " TABLE ( " ; J ; I ; " ) = " ; TABLE ( J I )
40 NEXT J
50 NEXT I
60 PRINT MAXI , TOTAL , COUNTS
70 END
;

●程式執行後顯示結果如下:

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 3 2
請輸入第 2 組條件 ? 5 3
請輸入第 3 組條件 ? 7 2
點兵點了 3 次,得到的答案為 23  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 7 1
請輸入第 2 組條件 ? 8 2
請輸入第 3 組條件 ? 9 3
點兵點了 55 次,得到的答案為 498  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 11 3
請輸入第 2 組條件 ? 12 2
請輸入第 3 組條件 ? 13 1
點兵點了 1 次,得到的答案為 14 

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 4
下列操作,將要求您每次以兩個分開的整數方式輸入 4 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 2 1
請輸入第 2 組條件 ? 5 2
請輸入第 3 組條件 ? 7 3
請輸入第 4 組條件 ? 9 4
點兵點了 17 次,得到的答案為 157  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 2
下列操作,將要求您每次以兩個分開的整數方式輸入 2 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 5 0
請輸入第 2 組條件 ? 6 1
點兵點了 4 次,得到的答案為 25  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 2
下列操作,將要求您每次以兩個分開的整數方式輸入 2 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 12 5
請輸入第 2 組條件 ? 18 13
點兵次數超過十萬次,還點不出答案,太不合理,故此題無解!  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 2
下列操作,將要求您每次以兩個分開的整數方式輸入 2 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 12 5
請輸入第 2 組條件 ? 18 17
點兵點了 2 次,得到的答案為 53  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 15 7
請輸入第 2 組條件 ? 21 13
請輸入第 3 組條件 ? 10 2
點兵點了 9 次,得到的答案為 202  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 5
下列操作,將要求您每次以兩個分開的整數方式輸入 5 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 11 1
請輸入第 2 組條件 ? 13 2
請輸入第 3 組條件 ? 15 3
請輸入第 4 組條件 ? 17 4
請輸入第 5 組條件 ? 19 5
點兵點了 18232 次,得到的答案為 346413  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 3 1
請輸入第 2 組條件 ? 5 2
請輸入第 3 組條件 ? 7 4
點兵點了 9 次,得到的答案為 67  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 8 1
請輸入第 2 組條件 ? 7 1
請輸入第 3 組條件 ? 5 2
點兵點了 7 次,得到的答案為 57  

ok
MAIN 
請輸入有幾個韓信點兵問題的已知條件  ? 3
下列操作,將要求您每次以兩個分開的整數方式輸入 3 組條件。 
條件輸入格式為:每一班有幾人在前,點完兵的剩餘人數在後,例如 10 2  
請輸入第 1 組條件 ? 7 3
請輸入第 2 組條件 ? 11 5
請輸入第 3 組條件 ? 13 8
點兵點了 26 次,得到的答案為 346  

ok

2026年8月2日 星期日

巧然天成的 RUN 指令

巧然天成的 RUN 指令


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 August 2026


實踐確實是檢驗真理的唯一標準,真正親自動手做過事情的人,才有資格講這句話,能講出這句話的人,也才能體會出真理的可貴。我是以整合許多名家作品的方式,設計出 ABC FORTH 數學計算系統,憑心而論,名家作品中的許多伏筆,均非淺顯而易見,設計當初,我自己並不完全知道系統竟然另外擁有許多性能,這樣的問題,唯有依靠實踐來檢驗,本文討論一些這種特性。

這個系統擁有遠大的前程,充滿著無限的發展希望,就如同 FORTH 程式語言可以無限制成長的本質一樣。但其意義並非單純的只是指令名稱可以無限的增加,也不是發展到可以直接應用於處理問題後就此作罷,而是顯現在系統無窮的性能增建能力上。這種吸引人的特質,經常會令我只顧勇往直前添加新的設計,不顧驗證已經完成的成果;只想拼命的增添系統性能,無暇管它到底能做甚麼?

如果仔細回顧我所參考過的名家設計,確實能夠體會出許多可圈可點的名家技術,但是,如果只是照抄這些技術,缺乏自己的組織構想,最後必然難成氣候,在我設計的系統中,可舉出來的這種例子很多。 ABC FORTH 數學計算系統內,由 FORTH 轉進 BASIC 式環境後,其中一個性能特異的 RUN 指令,就是一個最典型的例子。

我設計 ABC FORTH 系統時,最原始的根據是莫查理(Charles H. Moore)於 1981 年發表的論文,裴麥克(Michael Perry)隨後將其改寫成 fig-FORTH 的版本。此後,因為只須照抄,我們便能很快的將同樣設計轉進了 F83 系統,繼續維持它的存在。

翻閱這些過去的舊記錄,很容易看得出來,幾位名家都只將 RUN 指令,當作 BASIC 程式語言中,最原始定義的意義來設計。那種 RUN 指令,只能讓單一個 BASIC 程式被系統執行,與我所設計的 RUN 指令不同,我的 RUN 指令,允許無限個 BASIC 程式可以被系統執行。

因為,當時,大家只想驗證出 FORTH 很容易設計出 BASIC ,確實也辦到了,但沒有人想到要根據 RUN 指令的特性來改善系統性能。

近代 FORTH 大型系統中,健全的浮點數計算能力,以及由作業系統自然融入 FORTH 系統的中文顯示功能,是兩件吸引我選擇這個時機,在 FORTH 系統中重現 BASIC 功能的主要因素,其他大型 FORTH 系統優秀的特點都在其次。

2008 年,我在 Win32Forth 系統中完成加建 BASIC 功能的初期,並不看好這樣的設計。道理無他, 1964 年發明的 BASIC 舊觀念並不吸引人,舊觀念還有許多長期被人攻擊的缺點,例如: BASIC 中的 GOTO 指令,會令龐大的程式,形成麵條式的結構,自認為是電腦專家的人,就拿這樣的論點來嘲笑 BASIC 。我自己從不這樣批評 BASIC ,相反的,還特別鍾愛 GOTO 這種指令,道理無他,一切電腦軟體的基礎 -- 組合語言中,凡會跳躍分支的 jump 指令,都與 GOTO 相當。因此,無 GOTO 何來其他所有的程式語言?講句不客氣的話,結構化程式語言中,想添加能讓系統不出亂子的 GOTO 指令,根本很難設計,排斥 GOTO 的專家,是不是酸葡萄心理?確實難說。要不然,請以實踐檢驗真理,檢驗完了,大家也可以不用,以尊重您高尚的論調。

我常認為,自封為專家的人,都應該謹言慎行,他們的一舉一動,對社會影響重大,性能非凡的 GOTO 指令就是這樣被毀掉的。但是,請仔細想想看,多少人過去就用 GOTO 完成了多少偉大的事情? GOTO 對整個社會總的貢獻,絕對比任何一位自以為高尚的電腦專家要來得多多, GOTO 何辜?

為了這個 GOTO ,我長期思考對應之策,因此,系統設計完成之後,足足有半年時間,我並不想對任何人介紹我所設計出來的系統。思考的問題,就是如何解決被批評為『麵條式程式』?的問題,而且設定了自己的打算,如果解決不了,就不要推出自己的設計,因為歷史已經淘汰了傳統的 BASIC 。

最後,自己私下決定,經由 RUN 指令來代勞,改變它的特性,而且解決辦法精簡之至,僅只是大膽起用一個從不被列入作為標準指令,卻是每一個 FORTH 系統內必定都有的天王指令『INTERPRET』。此一自創附加技術,是一種巧然天成的安排,足夠擠進名家之流,讓我敢將自己的設計,加寫進入 Charles H. Moore 的名著,從此與他的技術共存。

在前已刊出的好幾篇文章中,可以見到 INTERPRET 指令的全盤精神,它主宰 FORTH 系統的一切。 FORTH 系統執行起來之後,系統等待著接受輸入。一旦有了輸入,就從系統中比對輸入是否為一個指令? 是,就將其執行掉,不是指令,就試著將輸入轉換成數字,轉得成就放在堆疊上,轉不成就反問使用者,這是甚麼東西?然後永無止境的繼續循環,這麼簡單的主宰工作,就由 INTERPRET 完成。

教科書不教使用者如何用它來設計程式?或如何發揮 INTERPRET 指令的基本精神?我在這裡傳播這種技術,是補技術傳承上的不足,只是聊表善意而已,沒料到好心有好報, 2011 年 4 月起我開始使用 ABC FORTH 系統設計一些略有規模的應用程式,便實踐出了許多 RUN 指令的好處。此前,這些特性,連我這個系統的設計人,自己都不知曉。

系統中的 BASIC 程式,原本只強調用來改善 FORTH 執行數學計算程式的能力,其他的功能,全依靠 FORTH 原有的基本性能,都能另外完成。這樣的思考方式,確實是輕忽了 BASIC 在 FORTH 系統中的地位了,好像只宜將數學計算交給 FORTH 中的 BASIC ,其他的都不適宜。但是,實踐大型程式的設計後,才發現,其實不然!

三年前,我只耗用了大約 6 頁的程式,設計出 ABC FORTH 數學計算系統。最近卻耗用將近 20 頁的規模,設計出一個性能優越的大型應用程式。為了增加使用上的彈性,還刻意盡量將大型應用程式,分割成最小單元的模組化格式。最後,就靠可以不限使用次數的 RUN 指令,來整理出更清楚明確的結果。 RUN 指令的內容,相當於只有 INTERPRET , RUN 存在於 BASIC 中時,就好像是 BASIC 可以叫用執行無數個 FORTH 系統。他的巧然天成特質,絕對能令人嘆為觀止。

以前,我在使用傳統 BASIC 設計大型信號處理程式時,經常苦於系統不允許使用者,可以同時開啟好幾個被順利執行的程式,而必須進行繁雜的人工操作。例如:我建成過一套系統,能夠自動讀進輸入硬體數據信號,系統可以選擇要不要進行信號的過濾處理?或直接進行從時域(Time domain)轉換成頻域(Frequency domain)的快速傅利葉轉換(FFT,Fast Fourier Transform),最後,還有決策程式必須執行。那時,一次只能開啟一個程式被 BASIC 系統執行,用起來痛苦得很。因此,每當見到 FORTH 同好在精研其他的 BASIC 系統時,我首先向他請教的問題,就是這個 BASIC 一次能跑幾個程式?答案是,就算行,數量也是非常有限的。 ABC FORTH 隨您設計,跑幾個都行,直到系統記憶體容量裝不下了為止,這種性能,靠的就是巧然天成的 RUN 指令。

這個 RUN 指令的性能,首先讓『麵條式程式』不見了,就按照電腦高尚專家的踹言踹語,設計符合他們要求的程式,我將 BASIC 大型程式全面模組化,僅只剩下幾個 GOTO 指令,自由使用於單一模組之內,以少於一個螢幕畫面的短程距離內自由跳躍,沒有看不懂的地方、沒有分析不了的流程、沒有不安全的例外、沒有陷入無限循環的可能,這些缺點,全都不會再在程式中發生了。

不再有開檔數量限制之後,許多程式也不用自己重新設計了,就大方的用滑鼠去複製別的程式,供自己使用。嫌別人純用 FORTH 寫成的程式,不容易看懂,日後會有追查上的麻煩,索性就直接黏貼在上或下方,然後邊追蹤、邊改寫成我的 BASIC 格式程式,結果是程式永遠可以看得清楚明白,更加好用了。這樣做,改成的 BASIC 程式,並不會增加記憶體的耗用量,也不會令執行速度變慢。因為,前引的列首編號不會被編譯進系統,而且 BASIC 式的一列程式,除數學式子之外,都只會被編譯成單一個 FORTH 指令而已,程式被執行的速度當然不會變慢。也沒有多耗用記憶體,這種情況,就形同原為橫式書寫的 FORTH 程式,現在改為直式書寫的 BASIC 程式,如此而已。

這兩段敘述,告訴大家,我改善了 FORTH ,同時也改善了 BASIC 。更有甚著,可以建議有心的讀者,仿照同樣方法,將別的程式語言建進 FORTH 系統後,改善它們的性能。有一位小老弟,熟 LISP ,他也留心國際舞台上用 FORTH 設計出來的 LISP ,何不試試我的建議?自己跟著創造出另外一套別有風味的 FORTH 中的 LISP 。也有人用 FORTH 設計出 C ,何不像我一樣?大膽起用巧然天成 RUN 指令的來源 INTERPRET ,建成之後,絕對能帶給您意想不到的效果。如此必能實現由 FORTH 來統管一切程式語言的性能,這些 FORTH 哲理的高度運用,絕對不是我一個人的專利,但也不是應該由我來實現它們的責任。

像我一樣,單只搞 FORTH 的同好,透過這篇網文,您熟讀融會貫通之後,也應該可以大大增高自己的 FORTH 功力。學一個沒人教您的 INTERPRET 指令,可以在 FORTH 系統中再執行 FORTH ,讓程式有多重層次性,這在除錯系統中的斷點式性能規劃上可以用到。也在 FORTH 駐留後再執行一個 FORTH 時,可以用到。其他還有許多例子可以舉出,用法是不勝枚舉,我寫不完。但請大家記住, INTERPRET 是每一個 FORTH 系統都有的指令,卻不是一個標準指令!

坐而言不如起而行,我就用 RUN 指令將 FORTH 翻譯成 BASIC 給您看。在 Win32Forth 系統中繪出柵格線原為 FORTH 格式單一指令 GRID 的程式如下:

 


: GRID
  green line-color
  0 0   moveto 800 0   lineto
  0 100 moveto 800 100 lineto
  0 200 moveto 800 200 lineto
  0 300 moveto 800 300 lineto
  0 400 moveto 800 400 lineto
  0 0   moveto 0   400 lineto
  200 0 moveto 200 400 lineto
  400 0 moveto 400 400 lineto
  600 0 moveto 600 400 lineto
  800 0 moveto 800 400 lineto
;

直接翻譯成 ABC FORTH 中 BASIC 式程式,但仍然還是 GRID 單一指令的新格式如下:

: GRID   BASIC
10 RUN  GREEN line-color
20 RUN    0   0 moveto
30 RUN  800   0 lineto
40 RUN    0 100 moveto
50 RUN  800 100 lineto
60 RUN    0 200 moveto
70 RUN  800 200 lineto
80 RUN    0 300 moveto
90 RUN  800 300 lineto
100 RUN   0 400 moveto
110 RUN 800 400 lineto
120 RUN   0  0 moveto
130 RUN   0 400 lineto
140 RUN 200   0 moveto
150 RUN 200 400 lineto
160 RUN 400   0 moveto
170 RUN 400 400 lineto
180 RUN 600   0 moveto
190 RUN 600 400 lineto
200 RUN 800   0 moveto
210 RUN 800 400 lineto
220 END
;



這還僅只是第一個直截了當,最簡單的範例。設計大型應用程式之後,還有更驚人的發現,總結下來,就是:

1. 凡是 FORTH 系統中可以單獨被執行的指令,都可以用 RUN 指令直接叫來使用,而且同時用幾次都無所謂。
例子無數:如 ABORT 、 PAGE 、 BEEP 、 SPACE 、 CR 、 HEX 、 DECIMAL 。。。。。 等都可以這樣用。

2. 凡是執行前只需提供數字,執行後不在堆疊上留下數字的任何指令,也都可以用 RUN 指令直接叫來使用,但每一列只限使用一次。
例如:上述所舉的程式翻譯例子 21 1 AT-XY 、10 SPACES 、1000 MS 、42 EMIT 。。。。。 等,可用範例也是不計其數。

簡而言之,這兩條規矩就是:一次 RUN 一個指令, RUN 完不留參數。

這裡僅只是隨便舉幾個例子,但請不要小看這樣的應用。

我一直強調,當初設計 ABC FORTH 系統,只是為了想改善 FORTH 不良於數學計算程式的原始性能,才選擇以 BASIC 格式達到目的,但並未設計出許多傳統 BASIC 該有的指令。例如:傳統 BASIC 中有一個將螢幕清空的 CLS 指令,根據上述實踐出來的性能,巧然天成的由 RUN PAGE 直接代換就能辦到。使用者想跳空三列後才印出執行結果時,不須要使用三次 BASIC中 的 PRINT “ “ 指令,可以改用 RUN CR CR CR 來完成。所有螢幕上游標的控制,與繪圖程式所有的操作指令,都可以直接由 RUN 指令叫用。這些發現出來的性能,代表我未為 BASIC 設計螢幕控制指令,卻由 FORTH 系統原來的性能自然融入了, RUN 指令怎麼不是巧然天成?

上述例子舉用了 RUN ABORT ,也是一項有意義的新發現。我在 2009 年十月刊出的一篇『實係數代數方程式的解法』(Solve algebraic equations of real coefficients 已撤文)網文中,為了要與日本教授 Yuji Kida 所設計的 UBASIC 性能比個高下,直接改寫他的程式,並要求一定要直接改寫得成,不幸遇到一個程式中可以隨便從有限循環中跳出來的指令,阻礙直接改寫,日本人將此指令命名為 cancel for ,當時,我不服輸,硬用系統測試出此指令的性能,結果,最後的解決辦法,是以定義出自己的 CANCEL-FOR 指令,其內容為使用四個 DROP 來達到目的。現在,實踐 ABC FORTH 後的經驗與觀念都更清楚了,事實上,只需在程式結束前,執行 RUN ABORT 便可以了。

為了發展大型應用程式,我必須參考許多很有用之 BASIC 、FORTRAN 。。。。。等舊有大型程式,發現裡面有很多這樣的程式設計方式,這個巧然天成的 RUN 指令,協助解決了這種困惑,當初,我並未為系統中的 BASIC ,設計出可以執行 ABORT 的功能,現在它自己跳出來協助我解決問題了,怎麼不是巧然天成?

自行使用 ABC FORTH 系統累積出來的實踐經驗,增強了我自己使用此系統的無比信念。Charles H. Moore 曾經公開質疑搞原子爐應用程式的人,為什麼要把程式寫成幾萬列?我從這個領域出身,雙方我都不予以否定。但是,現在,我卻絕對有信心,敢於明確的指出,如果搞原子爐的人,非得使用萬列以上的程式來求解問題時, ABC FORTH 數學計算系統,絕對能妥善的代勞,完成任務。

舉例而言,一年調整一次的爐心燃料,就得動到幾千幾百根燃料棒,根根都價值斐然,那麼,為了很有效率的營運這些燃料棒,使用萬列以上的爐心燃料營運程式,也就不足為奇了。這種程式,現在交給我來改寫成 ABC FORTH 系統可以直接執行得出來的程式,我也絕對辦得到。而且,日後發展 64 位元的系統普及之日,這種程式就不需要再行改寫,可以確保它 50 年能用。 50 年,是一個比任何原子爐可用壽命,都要來得更長的年限!我估計,只要勇敢承包這項工程,絕對能從中獲得豐厚利益,但卻是善事一樁,只是現已不喜言『利』,我只在乎如何提供 ABC FORTH 系統?能夠供給大家永遠使用。

除了 RUN 指令外,經由實踐,我還發現了其他也是巧然天成的一些性能,這種現象,意味著已經設計好的系統,還有許多隱藏的特殊性能,連我這個系統設計人都還不知曉,令我大為振奮,深覺更應該加強使用 ABC FORTH 系統來設計大型應用程式的機會,就能累積出更多的成果。

即時性交談(Real time interactive)性能,是 FORTH 與 BASIC 程式語言都具有的特性,也是別種程式語言所缺乏的特性。應用程式設計多了,就很喜歡使用這種一般程式語言不便於使用的特點。如此設計出來的程式,被別的程式語言使用者看過之後,想抄都很難抄去使用,因為它們不適合執行與使用者之間的即時性交談,因此更能顯現出 FORTH 程式語言與眾不同之處。

我們若將即時性交談,鎖定在只對鍵盤的輸入反應上(實際上,則可以是系統內的任何輸入訊息),大致而言,這樣的即時性對談,可以分成兩類,一是希望鍵盤未被碰觸過,則系統不作任何反應,碰了就立即反應。二是系統停下來,等鍵盤被碰觸過後,系統根據鍵盤被壓下之鍵來決定程式執行流程的走向。對應到這兩種狀況的 Win32Forth 系統原有指令為: KEY? 及 KEY 。

那麼,我們如何以巧然天成的設計方式,讓這兩種功能在 ABC FORTH 統的 BASIC 環境內實現出來?第一種狀況,我設計了一個簡單卻能讓程式可以隨時因碰觸了鍵盤便立刻中止執行的指令,稱作 TERMINATED ,內容如下:

INTEGER A$

: TERMINATED BASIC
10 LET A$ = KEY?
20 IF A$ = 0 THEN 50
30 PRINT “ Program has been terminated! “
40 RUN ABORT
50 END
;

如果您希望在一個可能停不下來的迴路內,可以因為鍵盤的碰觸而立即終止,就將 RUN TERMINATED 插入到您想要控制的指定位置就得了,但鍵盤都沒碰觸過,程式的執行就不會中止。請注意!設計 ABC FORTH 的 BASIC 部份時, KEY? 指令根本沒有被規劃進 BASIC ,而且還不必經過任何使用前宣告,便能如上例般直接使用,反而是用來暫存狀態的 A$ 需要先行宣告,可是原為只屬於 FORTH 的 KEY? 指令,卻巧然天成的進了上述 BASIC 程式環境,性能好得很。

同理,您若是希望程式執行到指定位置,便希望透過您是否壓下鍵盤的 Y 或 y 鍵,來決定程式是否要回頭重新執行一遍?設計方法,也是巧然天成的,可以直接引用 FORTH 系統中原有的 KEY 指令,予以完成,系統執行到 KEY 指令時,會自動停下來,碰了鍵盤就有後續結果,設計方式就簡單到如下列所示:


 
-----
-----
190 -----
200 LET A$ = KEY
210 IF A$ = 89 OR A$ = 121 THEN -10
220 -----
-----
-----

兩個完整可以直接使用的測試範例,則如下示:

INTEGER A$

: Y? BASIC
10 RUN PAGE
20 RUN 0 20 AT-XY
30 RUN CR
40 PRINT " Do you wish to print yes? (y/n)  "
50 LET A$ = KEY
60 IF ( A$ = 121 ) OR ( A$ = 89 ) THEN 110
70 RUN CR
80 RUN CR
90 PRINT " No "
100 RUN ABORT
110 PRINT " Yes "
120 RUN 1000 MS
130 RUN BEEP
140 END
;

INTEGER COUNTER

: RANDOM-COUNT BASIC
10 PRINT “ Counting and terminate test is starting now ! “
20 LET COUNTER = COUNTER + 1
30 RUN 1000 MS
40 LET A$ = KEY?
50 IF A$ = 0 THEN -20
60 PRINT COUNTER
70 END
;



感覺出『即時性交談』的效果了嗎?體會出『巧然天成』的意義了嗎?這就是 FORTH 與眾不同的本質, ABC FORTH 使用說明並不需要因此而另行改寫。

我們可以利用上述的幾個特別性能,應用於一個完整的範例程式,以展示從不用這些性能,到妥善利用這些性能,所顯示出來的差別。

此處所舉之例為一個單只根據三角形的三邊長度,來計算出三角形面積的公式,此公式稱為海龍(Heron)公式, Heron 是生活在西元 10 年至 70 年間的希臘數學家,此公式是為了紀念他,才被如此命名。

誰都知道三角形的面積等於底乘以高除以 2 ,這個基本計算方法當然比海龍公式還好用。但是,讀者請注意,古時候丈量土地,沒有現在這麼好的輔助工具,那時只能輕鬆的量長度。想輕鬆的確定一個直角,再量三角形的高度,就有困難了,亂量一通就會引發爭議。所以,大約2000年前的海龍公式,在當時丈量大面積土地時,確實是個很好用的公式。三角形可以作為一切多邊形的基本單元,因此,任何多邊形的面積,都可以只靠量長度而計算出來,所以海龍公式很有用。

要設計這個程式並不困難,我們拿它來作為範例的目的,主要是想顯示,隨便設計一下,也能求得答案。但想讓程式萬用,不怕輸入錯誤,就得經過一點執行程序上的安全處理。又進一步想讓萬用程式真能萬用時,這一章所討論的內容,就能拿來實際應用,利用控制鍵盤的輸入鍵來達到目的。

程式內已經有詳細的發展程序說明了,我們只須刊載整個程式,及其簡單的執行後輸出結果。


 

\ Heron’s formula
\ 海龍公式為一根據任意三角形的三個已知邊長,自動計算出面積的公式。
\ 三個邊長為 A、B、C,而 S=(A+B+C)/2 ,面積則表示為 DELTA 。
\ 公式為:DELTA= SQRT( S*(S-A)*(S-B)*(S-C) )

REAL A
REAL B
REAL C
REAL P
REAL Q
REAL S
REAL DELTA
INTEGER A$

\ 最精簡程式

: HERON1 BASIC
10 PRINT " Please input A and B "
20 INPUTR A , B
30 LET { P = ABS ( A - B ) }
40 LET { Q = A + B }
50 PRINT { " C must be > " ; P ; " and < " ; Q }
60 PRINT " Please input C "
70 INPUTR C
80 LET { S = ( A + B + C ) / 2 }
90 LET { DELTA = SQRT ( S * ( S - A ) * ( S - B ) * ( S - C ) ) }
100 PRINT { " delta = " ; DELTA }
110 END
;

\ 增加安全檢查性能

: HERON2 BASIC
10 PRINT " Please input A and B "
20 INPUTR A , B
30 LET { P = ABS ( A - B ) }
40 LET { Q = A + B }
50 PRINT { " C must be > " ; P ; " and <" ; Q }
60 PRINT " Please input C "
70 INPUTR C
80 IF { ( C > P ) AND ( C < Q ) } THEN 110
90 PRINT " It’s out of range, re-enter Please! "
100 GOTO -50
110 LET { S = ( A + B + C ) / 2 }
120 LET { DELTA = SQRT ( S * ( S - A ) * ( S - B ) * ( S - C ) ) }
130 PRINT { " delta = " ; DELTA }
140 END
;

\ 增加即時交談性能

: HERON3 BASIC
10 PRINT " Please input A and B "
20 INPUTR A , B
30 LET { P = ABS ( A - B ) }
40 LET { Q = A + B }
50 PRINT { " C must be > " ; P ; " and < " ; Q }
60 PRINT " Please input C "
70 INPUTR C
80 IF { ( C > P ) AND ( C < Q ) } THEN 110
90 PRINT " It’s out of range, re-enter Please! "
100 GOTO -50
110 LET { S = ( A + B + C ) / 2 }
120 LET { DELTA = SQRT ( S * ( S - A ) * ( S - B ) * ( S - C ) ) }
130 PRINT { " delta = " ; DELTA }
140 PRINT " Repeat? (y/Y) "
150 LET A$ = KEY
160 IF ( A$ = 89 ) OR ( A$ = 121 ) THEN -10
170 END
;

((
\ 執行記錄

FLOAD 'C:\Win32Forth 6.14.00\ISO2011\海龍公式.F'  ok
heron3 
Please input A and B ? 3 4.0
C must be > 1.000000000 and < 7.000000000 
Please input C ? 5.e0
delta = 6.000000000 
Repeat? (y/Y) 
Please input A and B ? 3.00 4e0
C must be > 1.000000000 and < 7.000000000 
Please input C ? 5
delta = 6.000000000 
Repeat? (y/Y)  ok

))



20260802 編後記:

1. 我在所有後來發展的 ABC FORTH 系統中,為了簡化連續使用 RUN 與 連續使用 LET 指令時的方便,從第二列使用 RUN 指令開始,可以只用 => 符號式指令取代,而且不必再用列號。同理,連續使用 LET 指令的情況相同,可以只用 :: 符號式指令取代,也不必再用列號。

2. 我在貼出本文前,刻意修改了 HERON 程式,令其可在 Lina64 Forth 系統中也能執行。修改成果與執行結果列示如下:


 
\ Heron’s formula
\ 海龍公式為一根據任意三角形的三個已知邊長,自動計算出面積的公式。
\ 三個邊長為 A、B、C,而 S=(A+B+C)/2 ,面積則表示為 DELTA 。
\ 公式為:DELTA= SQRT( S*(S-A)*(S-B)*(S-C) )

7 REALS A B C P Q S DELTA
INTEGER A$

: HERON BASIC
10 PRINT " Please input A and B "
20 INPUTR A 
25 inputR B
30 LET { P = ABS ( A - B ) }
40 LET { Q = A + B }
50 PRINT { " C must be > " ; P ; " and < " ; Q }
60 PRINT " Please input C "
70 INPUTR C
80 IF { ( C > P ) AND ( C < Q ) } THEN 110
90 PRINT " It’s out of range, re-enter Please! "
100 GOTO -50
110 LET { S = ( A + B + C ) / 2.0 E 0 }
120 LET { DELTA = SQRT ( S * ( S - A ) * ( S - B ) * ( S - C ) ) }
130 PRINT { " delta = " ; DELTA }
140 PRINT " Repeat? (y/Y) "
150 LET A$ = KEY
160 IF ( A$ = 89 ) OR ( A$ = 121 ) THEN -10
170 END
;

執行結果:

ching@ctt:~$ ./l

AMDX86 ciforth 5.3.0 
fload heron.f
A : ISN'T UNIQUE                                                
B : ISN'T UNIQUE                                                
C : ISN'T UNIQUE                                                
 OK
heron

Please input A and B 
? 3.0 e 0

? 4.0 e 0

C must be > 1.0 E 0  and < 7.0 E 0  
Please input C 
? 5.0 e 0

delta = 6.0 E 0  
Repeat? (y/Y)  OK




2026年7月16日 星期四

自創浮點系統的新問題

自創浮點系統的新問題


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 July 2026


1. 問題的發現:

我在修改2025年11月2日貼文『­誤差函數的應用』中之實際程式,以供自創浮點系統使用時,出現了問題,顯示原始程式無法順利通過編譯。

2. 問題說明:

自創浮點系統的規格,係2024年7月10日貼文『設計浮點系統』文中的格式,這個格式於 2025 年我回台灣與 FORTH 同好開研討會時,當場重建在 gForth 系統過,性能驗證無誤。因此,修改『數據分析應用程式』時,應該是只須要將所有輸入的浮點數字改成由三個單元形成的浮點數字便可。很不幸,未能通過編譯。

3. 除錯記錄:

經過操作 Forth 傳統的除錯方式後,發現未能通過的設計只存在於最後一個 MAIN 指令中,而且就是列號為 60 的程式,原程式轉錄如下:

60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( sqrt ( 2 ) * sigma ) }

特別說明 ! 上式本為只能在浮點系統中的寫法,開平方函數操作的對象 2 能夠這樣寫,係我專為 Win32Forth 系統設計 ABC Forth 時的特例,系統編譯時,能自動將所有的數字都自動處理成浮點數,但不處理陣列、矩陣、張量的整數指標。至於在其他 Forth 系統中加建的 ABC Forth 則均不具有這種功能。

除錯後第一次的改寫,寫成下式,很不幸,仍然不能通過編譯。

60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( sqrt ( 2.0 E 0 ) * sigma ) }

磨耗了許久,我才確定,問題的根源就是這種自創浮點系統的數字格式,會搞混數學運算先後秩序的編譯,於是再增加了一層小括號,令 ( sqrt ( 2.0 E 0 ) ) 可以被強制優先執行,最終落定的格式寫法如下:

60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( ( sqrt ( 2.0 E 0 ) ) * sigma ) }

4. 檢討分析:

顯然,我自創的浮點系統,確實有未能全面顧及所有狀況的缺陷,透過原始 Lina64 Forth 系統中的 SEE 輔助除錯指令,可以看到能被編譯成功的設計內容。單獨重寫一列小程式來檢驗編譯成果時,能夠發現以三元浮點規格輸入的數字,如果運算優先度處理不當,編譯後會出現 0 * sigma 結合成一組運算的現象,執行 MAIN 指令時就顯示浮點運算無法執行的提示。

5. 解決方法:

暫時先使用能強迫可以被最優先執行的成對小括號 ( ..... ) 來解決問題。

現在只能預想,我使用三元浮點數輸入法中的 E 指令,也必需宣告它被執行時的優先度,系統才能被正確的執行。

6. 題外話:

設計系統、使用系統、測試系統是一件冗長辛苦的工作,能夠發現問題的初衷,係我想將一些有用的程式重新改寫進最新設計出來的系統中使用。能發現新問題是好事,不要氣餒,必須直接面對問題、解決問題。

挑選這個程式來修改,也有原因。我向來擅長親自動手做實驗,也擅長對取得的實驗數據進行技術分析。數據分析程式的功能很有用,它不但能應用於實際科學,也能實際應用於日常生活。

原始程式中舉例的應用,係我在從事於鋁合金冶煉時的測試數據。我也曾將此技術應用於微弱中子源的測量讀數分析中,效果非凡,曾經用它來過濾掉幾根無效的中子探測器,並從中挑選出僅存的一根略為多加 100V 工作電壓(正常為 2400V ,勉強改用 2500V) 尚能使用的中子探測器。從事於固態燃料電池研究時,也用此技術來分析被測量材料導電度的實驗數據。用得最多的地方,則是我在從事於原子爐運轉擔任工程師時,核驗技術員抄表所得數據的合理與否?當然,這樣做容易引起當事人的不滿,所以,我通常都是默默地做,除非數據相當關鍵,影響重大,我才會取用這種有統計根據的分析結果,公開要求當事人慎重處理抄表所得。我記得有位學弟在測量中子的通量分佈時,出現一個被照射金片的測量讀數超出合理範圍,他的實驗結果則對日後一整年照射服務的演算影響重大,因此要求他務必找出問題的根源,結果他重新檢討了三天,終於找出問題所在,我這樣管理員工確實是苛求他了,但不得不做。

最近的日常應用,則是我用它來分析每天固定時間測得的血壓讀數。年紀大了,每天使用血壓計量血壓並檢討結果是一件很有益的事情。如果數據不太離譜,我只記錄。如果我認為不太合理,我就先透過誤差函數進行統計分析,看看得到的結果是否合理? 不合理時,大膽略去,重新測量。出現問題,我就檢討問題。後來,經常發現,問題主要出現在一開機就測量的讀數上,解決辦法就是首次的讀數就棄用,每次測量三次,大部份就都沒有問題了。

自測血壓本不是個人該做的事情,讀數不好時絕對會影響心情,專業醫療人員測血壓時都用聽診器聽脈博脈動的聲音。家庭自測用的數位式簡易血壓計所測得的讀數只宜供作個人參考,不要過份解讀,畢竟我不是醫療從業人員,又沒用聽診器仔細聽脈音,套上抽真空束帶時,難免偶爾沒放準脈博偵測點,讀數就有偏差了。自測血壓跟做實驗一樣,需要注意測量時的正確性、穩定性、不受干擾性。分析讀數的技術是重溫本領,用自己設計的系統、自己設計的程式解讀問題,讓很多事情變得很有意義。

每天測量血壓後檢討生活,最近的體會是發現速食麵、高分子蛋白的花生、市面販售的濃湯粉包、含鹽份過度的馬鈴薯片等,都是上了年紀的人不宜接觸的食物。我每次在當天貪吃之後,洗澡前測一下血壓,同樣狀況的結果必定多出大約 10 個讀數,隔天不吃了,血壓就能降回大約是 120/80 的正常讀數,每天記錄,屢試不爽。我也發現多吃了一些南瓜湯、紅豆湯、羅宋湯都不影響血壓,也不會增加體重。這些事都是我最近的生活,有程式應用上的樂趣,也有滿意於健康的心情。

新改寫成的程式與執行結果列示如下:


 

\ 數據分析程式
\ 分光儀測量鋁錠中鐵含量(%)十次,分析這十個測試數據是否合理的程式。
\ 作者:曾慶潭 2011/4/21 保有一切版權,知會作者,附加本宣告則允許引用。
\ Copyright 2011/4/21 Ching-Tang Tseng
\ Permission is granted by the author to use this software
\ for any application provided this copyright notice is preserved.

20 VALUE SIZE

SIZE ARRAY DATA

REAL AVG
REAL SIGMA

INTEGER TOTAL
INTEGER I

: INPUT-DATA
{{ DATA ( 1 ) = 121.0 E 0 }}
{{ DATA ( 2 ) = 122.0 E 0 }}
{{ DATA ( 3 ) = 138.0 E 0 }}
[[ TOTAL = 3 ]]
\ {{ DATA ( 1 ) = 1.52 E 0 }}	\ (1)輸入待分析數據。
\ {{ DATA ( 2 ) = 1.46 E 0 }}
\ {{ DATA ( 3 ) = 1.61 E 0 }}
\ {{ DATA ( 4 ) = 1.54 E 0 }}
\ {{ DATA ( 5 ) = 1.55 E 0 }}
\ {{ DATA ( 6 ) = 1.49 E 0 }}
\ {{ DATA ( 7 ) = 1.68 E 0 }}
\ {{ DATA ( 8 ) = 1.46 E 0 }}
\ {{ DATA ( 9 ) = 1.83 E 0 }}
\ {{ DATA ( 10 ) = 1.50 E 0 }}
\ [[ TOTAL = 10 ]]		\ (2)數據的總量為10。
;

: RESET-DATA BASIC
10 FOR I = 1 TO SIZE
20 LET { DATA ( I ) = 0.0 E 0 }
30 NEXT I
40 LET TOTAL = 0
50 END
;

: AVERAGE BASIC
10 LET { AVG = 0.0 E 0 }
20 FOR I = 1 TO TOTAL
30 LET { AVG = AVG + DATA ( I ) }
40 NEXT I
50 LET { AVG = AVG / I>R ( TOTAL ) }
60 END
;

: DEVIATION BASIC
10 LET { SIGMA = 0.0 E 0 }
20 FOR I = 1 TO TOTAL
30 LET { SIGMA = SIGMA + ( DATA ( I ) - AVG ) * ( DATA ( I ) - AVG ) }
40 NEXT I
50 LET { SIGMA = SQRT ( SIGMA / ( I>R ( TOTAL ) - 1.0 E 0  ) ) }
60 END
;

: REPORT BASIC
10 RUN CR
20 RUN RESET-DATA
30 RUN INPUT-DATA
40 RUN AVERAGE
50 RUN DEVIATION
60 RUN CR
70 PRINT { " 平均值(Average value) = " ; AVG }
80 RUN CR
90 PRINT { " 標準誤差(Standard deviation) = " ; SIGMA }
100 RUN CR
110 END
;

REAL erfx   REAL erfy
REAL T   REAL D   REAL AUX

: ERROR-FUNCTION BASIC
10 LET { AUX = ABS ( erfx ) }
20 LET { T = 1.0 E 0 / ( 1.0 E 0  + 0.3275911 E 0  * AUX ) }
30 LET { D = EXP ( NEGATE ( erfx * erfx ) ) }
40 LET { erfy = 1.0 E 0  - D * T * ( 0.254829592 E 0 + T * ( -0.284496736 E 0       
         + T * ( 1.421413741 E 0  + T * ( -1.453152027 E 0  + 1.061405429 E 0  * T ) ) ) ) }  
50 IF  { erfx >= 0.0 E 0 } THEN 70
60 LET { erfy = NEGATE erfy }
70 END
;

REAL TIMES
INTEGER Discrimination
INTEGER FLAG
1.41421356237309 E 0 fconstant sqrt2

: MAIN BASIC
30 LET FLAG = 0
40 RUN REPORT
50 FOR I = 1 TO TOTAL
\ 60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( sqrt2 * sigma ) }  
60 LET { erfx = ( ABS ( DATA ( I ) - AVG ) ) / ( ( sqrt ( 2.0 E 0 ) ) * sigma ) }   
70 RUN ERROR-FUNCTION
80 LET { TIMES = 1.0 E 0 / ( 1.0 E 0 - erfy ) }
90 LET Discrimination = INT ( TIMES )
100 IF Discrimination > TOTAL THEN 120
110 GOTO 160
120 LET FLAG = FLAG + 1
130 PRINT " 統計分析顯示,數據 DATA( " ; I ; " ) = " ; { DATA ( I ) ; " 有問題。 " }
140 PRINT " 此值之誤差量,應該是測量 " ; Discrimination ; " 次以上才有可能出現一次。 "
150 PRINT " 但是這次測量, " ; TOTAL ; " 次就出現一次了,故有問題。"
160 NEXT I
170 IF FLAG <> 0 THEN 190
180 PRINT " 根據誤差函數核算結果顯示,所有數據的誤差量均在合理範圍之內。 "
190 END
;


\s

執行結果:

ching@ctt:~$ ./l

AMDX86 ciforth 5.3.0 
fload dataanaly.f
DATA : ISN'T UNIQUE                                                
I : ISN'T UNIQUE                                                
D : ISN'T UNIQUE                                                

Programs above \S have been loaded. 
 OK
main

平均值(Average value) = 1.27 E 2  

標準誤差(Standard deviation) = 9.5393920141 E 0  

統計分析顯示,數據 DATA( 3 ) = 1.38 E 2  有問題。 
此值之誤差量,應該是測量 4 次以上才有可能出現一次。 
但是這次測量, 3 次就出現一次了,故有問題。 OK


2026年7月2日 星期四

創造與實踐特別指令

創造與實踐特別指令


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 July 2026


創造(CREATE)與實踐(DOES>)兩個 FORTH 標準指令結合在一起使用時,形成一種任何其它程式語言所缺乏的特殊現象。

我將以特殊的手法介紹 CREATE ..... DOES> 指令組。就學習難度分類,這一組算是三級的難度,比二級的 WORD、NUMBER 難一點,比四級的 Multitasking 或 Meta-compiling 觀念簡單一點。

古時候, CREATE 的名稱為 <BUILDS ,而且設計出指令的方法有一點差異, 79 標準以後指令名稱就修改並固定成 CREATE 了。在台灣的 FORTH 同好, 1980 年代使用 AIM65 壓克力封裝的透明電腦、追蹤最原始被公開的 fig-FORTH 系統源程式、閱讀古老的 FORTH 技術文獻 ..... 等狀況時,還有機會見到被使用的 <BUILDS 名稱。我在設計 ABC FORTH 數學計算系統時,也久別重逢了這個指令,所依據的最原始程式,就用 <BUILDS 而不是 CREATE 。

我不跟寫別人的教材,如果沒有自己獨特的一套解說方法,來強化大家學會打算談論的主題,就不該浪費讀者閱讀本文的時間,所以,我會使用特殊的手法寫 FORTH 教材。

我的網文教材中大部份只有淺顯的觀念,但直接切中難了解的相關 FORTH 指令,教會您實際的使用方法。至於一般 FORTH 老手喜歡探討的指令詳細結構,我完全不提。因為,我知道,有一天,您渡過了使用 FORTH 創作程式的初期困難後,會主動的去追蹤每一個指令的細部結構,不需要別人督促。您若不能渡過那種困難,就不會進一步的想深入了解 FORTH 內在系統的結構。因此,教您那些東西,有可能是在浪費時間。現在,我們仍然從外在的使用觀點,經由特殊觀念了解 CREATE ..... DOES> 。

這份教材,調皮的部份,取材自 comp.lang.forth 網頁的一些討論,如果您英文已經看得很順當了,我也介紹您直接去參訪,它不是一個只有我能獨享的 FORTH 科技園地。另外,請不要輕忽那些討論內容,我尊重所有的專家,所以會有『網文』可能比『論文』來得快又來得重要的論點,這是莫之能禦的時代趨勢,老頑固、老學究堅持抵制說『它不正式』也沒有用,頭腦靈活的人早就經常拿這些技術先行實現了許多夢想,寫 FORTH 網文的好手,從來就不在乎公開任何好技術後,會失去可以取得成為博士論文的機會,他們在嘉惠大眾,我因佩服而學習他們。

一般而言,純用電腦專業術語來解釋 CREATE ..... DOES> ,可能還不夠強烈,學習者就不易搞清楚它們的意義,不能記住使用方法,也就難以用來設計特異程式,發揮別種程式語言所沒有的效果。靜宜大學的金城老師主要專長就是電腦,我們曾談過簡化這一組指令的中文解釋,他說:『我宣告一個資料結構,就是 CREATE ,而且決定要怎麼用就怎麼用,就是 DOES> 。』解釋得非常好,大家不妨就這樣記住它們的意義。可是,奇怪的很,他在自己的著作『FORTH 的禪思』第八課中,卻不用這麼言簡意賅的解釋,反而寫了一大堆說明。也許是著作付梓之後,透過交談,才激發出他的簡化潛能,他有空時確實該來與我們多聊聊天,有好的話語,我會幫他記述。

為了讓指令的適當中文名稱能通順的使用於文句中,符合中文文法,以便在傳承技術或進行程式說明時感到方便,我則給予這兩個指令下列中文譯名:

CREATE 創造(資料結構)
DOES> 實踐(後續指令)

此處仍是列而不議(爭議),讀者請笑納便可,我們的重點在只談技術。

這兩個中文名稱又好又偉大,它們的功能確實也是如此,誇張一點形容,單憑這一組指令的功效, FORTH 的性能就足以蓋過其他現行的任何程式語言,寫程式時還能隨心所欲的『創造』和『實踐』,豈有不被稱為最偉大的道理?

為了更清楚解釋它們的性能,我要離題另行介紹一些機械工程學方面的知識,引用這些知識來解釋,就能更加明白這種指令的性能,這樣子學習,有助於強化您的記憶,也增加您對機械工程方面的認識。

學機械工程會見識到許多重型的產業生產用機器,有一些機器被冠以『工作母機』的美好名稱,例如:古時候被稱作車床、銑床、刨床、磨床、鑽床、研床、搪床…者(簡稱:車銑刨磨鑽研搪),均可算是工作母機。其他的機器就不能被納入工作母機的行列,例如:沖床、夾床、壓床、軋床、鍛床、鋸床、油壓機、輸送機、放電加工機、射出成型機…等。這兩者之間有甚麼區別?簡而言之,生產出來的工件成品,可以直接當作其他機器之零件者,才能被稱之為工作母機,例如:車床車製出螺絲,可以給別的機器當零件使用,車床就是。反之,沖床沖製出金屬鍋、碗、瓢、盆,不能給別的機器當零件使用,沖床就不是。『工作母機』就是機器的母級機器,提到她會肅然起敬。

時代在進步,近代的工作母機有了一點變化,例如:機械引擎的活塞缸套,已經發展出了快速、簡便、經濟的生產方法,用搪床來搪缸的機械加工就減少了,這個工作母機也就逐漸式微而少見。又如:以銑刀銑製金屬工件表面的技術,發展得相當進步,製品的品質,已可媲美於傳統龍門刨床刨製出來的金屬表面要求,銑床可以同具刨床的功能,於是刨床也逐漸式微而少見了。現行機械工廠中可以人工操作的工作母機,大約只剩車床、銑床、鑽床三種機器,屬於必備,其他的則可以功成身退了。機器配上了協助自動化的電腦,則改稱 NC 工作母機, NC 是數值控制(Numerical Control)的意思,因為用來操控機器的電腦程式中,大量的使用指揮機器定位或定速所須要的數值,故得其名。機械工程系的學生,學習機械製造這一門科目時,會學到上述這些知識,進行工廠實習時,就會直接接觸這些機器。如果您希望一生都有機會運用機器來創造東西,那麼,大學就該選讀機械工程系,現在則專心談論我們的 CREATE ..... DOES> 兩個 FORTH 指令。

實際用過 FORTH 設計較高級程式後,您才會發現,多工程式(Multi-task)中的 NEWTASK、ALSOTASK 這種指令組,與現在正要討論的創造(CREATE)、實踐(DOES>)此一指令組,都像機械工程學中的工作母機,它們形同為『造指令的指令』,也就是它們都用來設計『零件指令』,再供『應用指令』使用,『應用指令』才能夠方便的被執行,它們就形同是『母級指令』,用到它們當然會肅然起敬。接下來,我們逐步介紹運用這組指令的使用範例。

FORTH 中的標準 VARIABLE 指令,也可以用上述 CREATE ..... DOES>『母級指令』來設計,這是最簡單的這一組指令使用範例,例如按照下列程式逐列操作:

: VARIABLE1 CREATE 0 , DOES> ; ok
VARIABLE1 TEST1 ok
18 TEST1 ! ok
TEST1 @ . 18 ok

這個 VARIABLE1 的功能,就與 FORTH 中的標準變數宣告指令 VARIABLE 完全一樣了。

其中, TEST1 相當於是一個『應用指令』,可以在程式中到處應用,它須要使用 VARIABLE1 這種『零件指令』才能宣告形成,而『母級指令』 CREATE ..... DOES> 則可以完成『零件指令』 VARIABLE1 的設計。

仔細追蹤 VARIABLE1 『零件指令』的設計內容,從創造(CREATE)資料結構開始,系統首先根據使用者隨後使用時,賦予的任意名稱 TEST1 ,在系統中創造了一個變數的資料結構,而且先將資料內容歸 0 ,創造到此結束。此後,每逢 TEST1 變數被應用而執行時,系統就實踐(DOES>)後續指令,此簡例中沒有任何指令,於是實踐(DOES>)就只執行將創造完成的 TEST1 資料結構之參數欄起始位址,放在系統數據堆疊上,僅此一項工作為實踐(DOES>)天生非做不可的實踐內容,實踐到此結束。

以前,有一個馬戲團小丑,在表演模仿走鋼索前,把地板掃一掃,想放置脫下來的外套,可是等他脫下外套後,卻把外套丟到很遠很遠沒掃過的地方去了。小丑的調皮,可以引起觀眾開懷大笑。我們也來學一學小丑,調皮的不按規矩使用這一組母級指令,依序執行下列程式,就能看到它們的效果:

: NAUGHTY1 CREATE DOES> DROP 1 . ; ok
NAUGHTY1 TEST2 ok
TEST2 1 ok

這組母級指令造好的 NAUGHTY1 零件指令,被 TEST2 應用指令使用時, TEST2 根本沒有資料結構的體部,也就是根本沒有內容,應用指令 TEST2 每次被執行時,卻都拓印出 1 ,它天生非做不可的實踐內容,實踐完了就立刻被丟棄不用了,這個程式就像上述的小丑,有一點調皮,卻產生了一點特殊效果。

既然調皮有點看頭,那就進一步調皮一點,看看使用兩組實踐 DOES> 配一組創造 CREATE 的效果,又會如何?依例逐列執行程式,得到了下列結果:

: NAUGHTY2 CREATE DOES> DROP 1 . DOES> DROP 2 . ; ok
NAUGHTY2 TEST3 ok
NAUGHTY2 TEST4 ok
TEST3 1 ok
TEST4 2 ok

您可以跟著調皮下去,看看續用這個 NAUGHTY2 零件指令,多宣告出幾個應用指令,會有甚麼結果?還有更調皮的設計,設計成下列:

: NAUGHTY3 CREATE BEGIN DOES> DROP 1 . DOES> DROP 2 . AGAIN ;

注意! BEGIN ..... AGAIN 是一個無限循環,這樣子設計程式,執行起來竟然可以不毀系統,而且您還可以照常在 ABC FORTH 系統中繼續發展程式,請您自己試一試這些現象,或者想辦法比上述所有程式更加調皮,看看能有甚麼結果?

我不聰敏,拿這樣的範例來應用,仍然可以設計一個上課點名遊戲程式,只是學生都是虛擬的存在電腦中。

\ 母級指令 CREATE ..... DOES> 測試程式
\ 作者:曾慶潭 2010-02-16

: 男學生 CREATE DOES> DROP ." 有!我是男的。" ;
: 女學生 CREATE DOES> DROP ." 有!我是女的。" ;
: 兩性生 CREATE BEGIN
DOES> DROP ." 有!我是男的。"
DOES> DROP ." 有!我是女的。"
AGAIN ;

\ (( 使用範例:上課點名遊戲程式
男學生 湯姆
女學生 瑪莉
兩性生 喬治
\ ))

您可以實際去執行一下,分別鍵入學生的名字後,看看結果。『喬治』則要多點幾次名,他(她)真的會忽男忽女,好像有一個名叫『喬治男孩』的明星就是這樣,他(她)們有公開存在於現今世界的權力,不要以異樣的眼光看待他(她)們。我們的 FORTH 真正是一個既古老又先進的程式語言,它憑著創造(CREATE)與實踐(DOES>), 在 58 年前(1968),就把今天這個時代的爭議性問題處理得這麼好。

談起如此的一組 FORTH 傑出指令,實在可以創作出無限的調皮話題。

我所設計的 ABC FORTH 數學計算系統,更是靠著它們才得以完成,系統裡面的所有變數結構都在變化。變數出現在等號『 = 』的左邊或是右邊時,性能就得不同。變數若使用於浮點數的環境,如果還得用到指標,它的指標就只能為整數,有幾個整數還得視情況而定。這麼囉嗦的要求,如果不用調皮的手法,可能很難完成設計。

能用上述模式解釋這一組傑出指令,平生就得多學、多用、多涉獵各方面的知識,頭腦才能有豐富的聯想力來進行描述,讀者可能看似簡單,實則未必,要強調這樣的論點,可以透過一個別人講過的笑話來形容它,您自己體會,同時結束本文。

一個幼年私塾,有一班幼童背頌了一段課文,內容是:
『人坐著吃飯,馬站著吃草。』
過了一會兒,私塾的老先生要幼童起來背頌這篇課文,小華先背,可是站起來就怕老先生用戒尺處罰,於是背出了:
『馬坐著吃草 ..... 』
大家都笑了,老先生有點生氣,用戒尺從輕發落了一下小手心就算了,換小明站起來再背頌,小小的心靈真可憐,同樣的狀況:
『人站著吃飯 ..... 』
老先生再輕輕的打了小明一下小手心,讓他坐下,然後說:
『這麼簡單都不會,現在大家把書本合起來,都跟著我唸。』
『人坐著吃馬 ..... 』

這兩天紐西蘭夜間的氣溫低於 0 度 C ,我準備貼文時確實有點冷。

2026年6月16日 星期二

蛻變編譯

蛻變編譯


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 June 2026


本文透過簡單的生物學原理,講解 FORTH 系統中,以蛻變編譯(Meta-compile)生長技術,發展出新系統的基本觀念,很適合作為了解並永久記住操作蛻變編譯時的依據。

日本的心理學家新崎盛紀,在他的著作『直覺力』中,以機能定義『創造』為:『把性質相異的素材,重新排列組合成前所未有的新事物。』,言簡意賅,本身就創造了『創造』。

創作則是創造作品,會有物品產出,它代表著成就,令創作者愉快。好的作品還能用來造福人群,美化世界,這樣的創作就值得鼓勵,而且越多越好。我喜歡創作,也喜歡別人的創作,更喜歡見到專家那麼簡明扼要的描述『創造』。

年輕時,自覺見聞、知識、經驗與觀念都有所欠缺,創作嫌早;年邁後才想要創作,可能就會嫌晚而力不從心;現在恰似正逢其時,該使出渾身解數實現自己的喜好了。已經有許多年的時間,我似乎同時進行了兩項創作,一是文章,另一則是電腦程式系統,兩者皆我所好,總想不要顧此失彼,二者可以兼得,這個網頁貼文的內容因此就由兩者揉合而成。我很愛惜自己的作品,也很希望能與世界互動,並且隨時反省、把握住作品要有正面的效果,讓讀者受益。

回顧 ABC FORTH 數學計算系統,真可算是符合前述『創造』的定義,那裡面實在沒有太多純屬於我個人的作品,在著作書本以便留下設計技術時,我寫過:『我是站在巨人的肩膀上往前看』,表示我取用了大量性質相異的素材,素材早就已經存在,重新排列組合出『ABC FORTH數學計算系統』,它確實是前所未有的新事物,這裡面甚至於擁有許多我自己都還不曾經歷過的使用性能。

創造或創作並不困難,它不是要你從事於『無中生有』的事情,而是要你從事於『有中生好』的工作,重點在構思如何重新組織與安排出別人辦不到的結果。這樣的解說提醒大家,為了人類的福祉,要多行有意義的創造與創作。

『生命』是人人愛惜的,才會有人喜歡養寵物、栽盆景,因為這些東西都有生命特徵,『活潑可愛』通常都是我們對這些喜歡對象的主要描述。根據這個道理,我很可以使用一種很有創意的方式,敘述 FORTH 為什麼能被大家喜愛?因為,我發現 FORTH 相對於其他程式語言,具有別人所沒有的『生命特徵』。

要解釋這樣的觀點,必須引用生物學上對『生命』所下的定義。 FORTH 扯上生物學並不離譜,但不是『電腦』與『人工智慧』上的那種關係,我有我的特殊看法,會有這種創意看法是其來有自。

我從台灣桃園武陵中學高中畢業,武陵中學是一所不差的學校,當年高中一年級時,總共有十個班,一班平均約四十幾個學生,蔡惠美老師教我們生物學,我還記得她把期末考的試題出得非常難拿高分,全用複選題,選完還得寫理由,寫錯了每題都會倒扣。

高中二年級起,全校學生要開始重新分班,根據未來打算選讀大學科系的志向,分出不同的班級,分成自然、文史、農醫、法商四種類別,各自加強學習各別不同的科目,面對大學入學考試,進行不同的選擇。

高一考完期末考後,我才得知,全校只有兩個學生,期末考生物學的分數是及格的,滿分是一百分,六十分及格,我考了七十六分,另外一個後來成為醫生的同學,考了六十一分。蔡老師特別問我是否想唸當年分類為丙組的農醫類組,我恭敬的回答想唸理工科學類別,屬於甲組的自然類組,答案當然令老師失望,不過我確實熱愛所有的自然科學,包括博物學與生物學,至今我仍有能力自己閱讀這方面的書籍。有過這一段歷史,所以我會有 FORTH 與生物學方面的獨特看法。

所有生物學基本教材第一章都討論有關『生命』的現象,你若不熟,可以找一本淺顯的生物學教科書來回憶一下,它很簡單,只是說明,並不難懂,我自己是特別了一點,會牢記它,還會推敲它。一般而言,生物學家都認為生物可以歸納出四種生命特徵,也就是新陳代謝(metabolism)、生長(growing)、感應(sensitivity)與繁殖(re-production)四種生命特徵,我小女兒提醒過我,所有生物都該有另一項『死亡』(mortality)的特別現象,我也覺得合理,應該納入,生物學書本沒這樣寫。

我們可以根據上述的生命特徵來探討 FORTH ,你就可以發現人人喜歡有生命的東西,如果你了解 FORTH 也有生命特徵,它一旦被了解後,自然就會被喜歡,也就不足為奇了。

一般的程式語言系統,還不曾有過被設計成像 FORTH 這樣的結構,也許將來會有,但此前沒有。舉凡市售的程式語言系統,例如:ALGOL、COBOL、BASIC、FORTRAN、PASCAL、ADA、C、LISP、PROLOG、ASSEMBLY ..... 等等我曾接觸過的程式語言,結構都不同於 FORTH ,最大的不同是這些系統整體結構絕對是死的,不管它是以那一種編譯器(Compiler)或執譯器(Interpreter)設計而成的程式語言,它們的編譯器或執譯器是絕對不允許使用者亂動的,誇張一點說,是不容你動它的一絲、一毫、一根汗毛,那怕是僅改一個碼(Code)都不行,所以我很誇張的形容它們都是死的,一點都不為過。它們從大公司出廠的那一天開始,就被製成像標本一樣的死東西,可能很好看,使用者卻絕對不能從它感受到任何一項上述的生命特徵,或要它活過來。請問你以前使用過的任何上述程式語言系統,它的屍體如果仍然存在,它有任何一丁點的改變嗎?沒有,它確實是死在那裡,或者是逝去得無影無蹤了。

回頭來看 FORTH ,它那一點像有生命特徵的樣子?有的,所有的生命特徵它還全都有呢!只差它不會死亡,否則它就令人感到可怕而不敢喜歡了。

生物的新陳代謝,講的是生物攝取食物,獲得養份與能量,過程中有些許物質交換,生物處理源源不斷的輸入,進行活動並排泄廢物。

西藏有一種傳說中的動物被雕刻成飾物,名叫皮丘,傳言牠吃東西,只進不出,愛打麻將的人如果將此飾物帶在口袋裡,象徵一定贏錢,而且只進不出,據說萬一被對手發現,會被痛揙一頓,然後輸贏全都不算。皮丘只是傳說中的動物,不是真的,牠不會新陳代謝,所以不能被介定為生物。

FORTH 就新陳代謝此一特點而論,它是擁有這項特徵, FORTH 一旦在電腦上運轉起來,它的運轉方式就一直模仿著生物的新陳代謝特徵,處理源源不斷的輸入,如果輸入是指令,它會從自己的本體內比對出來,然後就依指令的內容,將此輸入以執行方式消化掉,就好像是取得了外界的能量,進行了一次相當健康的活動,最後愉愉快快的通知使用者一個『ok』,顯示在螢幕上。如果輸入是數字,它就將數字轉換成它自己消化後可以取用的型式,暫時存放在系統內的數字堆疊上,留供後用,這也是另一次系統的活動,有些許的物質交換,我們還可以強調這像是它的系統內部起了變化。如果輸入是一堆垃圾,它也認得,它就拒絕接受,然後不客氣的問你,這是甚麼?自從你會操作 FORTH 系統之後,你不覺得你因 FORTH 會這樣子進行新陳代謝而感到喜歡它嗎?

關於生命特徵的第二個生長現象,對 FORTH 而言更是特別。前已述及,別種程式語言系統的內部是不容更動的, FORTH 不同,它被設計成像生物一樣,隨時可以生長。每當你用冒號定義直接設計 FORTH 指令時, FORTH 的系統就在生長了,除了從冒號 『:』 到半冒號 『;』 之間所定義的程式指令,可以令系統成長外,還有變數『VARIABLE』、常數『CONSTANT』、字彙『VOCABULARY』…等的宣告,以及低階指令從『CODE』到『END-CODE』之間設計的程式指令,也都會令系統成長,而且確確實實與系統紮實的長在一起, FORTH 才能繼續『活潑可愛』的活動下去。另外還有特殊的嵌碼『,』入系統、配置『ALLOT』一段記憶體空間 ..... 等指令,也能令系統成長,由檔案載入程式,長的更快。如果長得不好,還可以使用忘掉『FORGET』指令,將不想要的部份一口氣就排泄掉,某些 FORTH 系統還能使用一個簡單的特殊指令,將現行系統立刻固定下來,Win32Forth系統使用的是指令『FSAVE』。你看, FORTH 怎麼沒有生長特徵?請注意!所有程式都跟系統長在一起, FORTH 只有一個整體式的結構,它會生長,統統長在一起,生物的生長不都也是這種長法嗎?別的程式語言系統,行嗎?

更有甚者, FORTH 的生長特徵是可以修改的,我所創作的『ABC FORTH數學計算系統』,如果不對原始FORTH母系統Win32Forth的核心部份,在生長時進行修改,不可能達到符合要求的性能設計,我只是 FORTH 的老手不是高手,就能辦到此事。我的這一生也搞過其他幾種程式語言系統,但都可以確信,此生是別想在別的系統上動任何念頭了,如果還有來生,那麼,來生再看看吧。

談到生命的第三種特徵──感應現象,是生物得對環境的刺激有所反應,才能稱之為具有感應能力, FORTH 號稱具有立即(Real time)響應能力,指的就是這種能力。操作 FORTH 系統時,你所鍵入(Type in)的每一個字元,都由 FORTH 系統內的執譯器(Interpreter)立刻進行處理,是 FORTH 系統直接對輸入進行反應,是 FORTH 隨時都對刺激它的環境訊息進行感應。要它執行有感應能力的程式時,也可以利用特殊指令『KEY』或『KEY?』直接對鍵盤輸入進行反應。若要設計對電腦周邊的感應程式,當然也比當前任何其它種程式語言系統容易,我們過去幾十年,已經設計過許多這種程式,只有又快又好的感覺。別種程式語言也能用來設計有感應功能的程式,但請注意!是被譯成的程式可以感應,不是別種程式語言系統自身在做感應。

第四種繁殖能力最為特別,一般程式語言系統要想具有繁殖能力,簡直就是天方夜譚、不可思議,賣系統給你的廠商,根本不為系統設計生殖器官,要不然他們就不能繼續賺你的錢了,因為你一定會用首次買到的系統來生出新系統,他們以後怎麼可能再賺到你的錢?微軟的老板就因為這樣而成為有錢的人。作業系統中雖有 COPY 功能,可以讓使用者省下一點還得再買同一系統的錢,但也只能複製程式,絕非繁殖系統。

程式語言系統的生殖器官難道設計不出來嗎?不是的,絕對可以辦到,只是除了 FORTH 以外,別人確實是很難設計出來罷了。因為生殖方法牽扯到生殖的哲理,僅有生殖器官,若沒搞清楚生殖的標準方法,還是生不出新的系統。此前,仍然只有 FORTH 的哲理內包括了生殖哲理,它還沿襲了生物的生殖方式來進行生殖,是唯一具有生殖特徵的程式語言。

生物要能生殖,他的自身首先就得依功能進行高度的分化,然後又要求生命的最基本單元要統一成單純的結構,也就是所謂的單個生物細胞結構都得類似。

想一想你自己的生命,你有高度分化的器官,卻都由細胞組成,細胞雖然不同,其基本結構則都一致,例如:細胞核內有遺傳染色體(Chromosomes)、細胞體內有粒腺體(Mitochondria)、高基氏體(Golgi bodies) ..... 等等,不同器官的不同細胞都必須要有一種類似的結構,如此,生物才能生殖,因為生殖要從單一個胚胎細胞開始。

再想一想 FORTH ,漂亮健全的 FORTH 系統,也有依功能進行高度分化的程式內容,例如:檔案處理、浮點運算、繪圖功能、編輯程式、甚至於等同於生物生殖器的繁殖程式(Meta-compiler)…等等,他們分別被歸類到相關的器官字彙(Vocabulary)內,高度的分化了。但不管各個程式的功能如何,組成它們的最基本單元,也就是基本指令,在 FORTH 中稱之為一個字(Word)者,其結構也都完全統一,只是內容不同。也可視同是最基本單元的常數、變數、字彙 ..... 等,他們的結構也都跟最基本的指令一樣,只有所謂的名稱欄、連結欄、解碼欄、參數欄 ..... 等相同結構。 FORTH 的設計哲理,安排成與生物生存的哲理完全一致, FORTH 就容易擁有生殖特徵。基本細胞結構一致,生物就容易完成生殖;基本指令結構一致, FORTH 就容易也就會生殖。

除此之外, FORTH 的生殖方法,也與生物的生殖方法類似。生物在胚胎生長時,先從單一個胚胎細胞開始,然後進行細胞分裂,在到達某一固定數量前,分裂出來的細胞不做功能分化。我特別請教台北醫學大學的微生物老師商惠芳博士,驗證這種現象,她明確的告訴我,確實如此,生物在繁殖時,細胞分裂到 2 的 5 次方,也就是 32 個以前,是不進行功能性分化的,此後才進行功能性分化。 FORTH 繁殖方法亦然,它也先長出一個不進行功能分化的核心(Kernel)程式,再用這個核心程式去長出各個功能不同的程式,以形成一個健全的新系統。

FORTH 有許多表徴現象,與我們熟悉的生物生命現象如此的類似,絕非偶然,當初發明它的莫查理(Charles H. Moore)先生將其設計成這個樣子,他所想像出來的FORTH 基本哲理,在有意或無意中應該是參考了生命現象而成,這樣形成的可貴哲理,有助於 FORTH 的恆久存在,因為我們熱愛生命,只要你能了解 FORTH ,顯現許多生命特徵的 FORTH ,自然就能為你所愛。

自古以來,我曾接觸過的好幾套 FORTH 系統,它們生殖過程的操作方法也都類似,Win32Forth 系統亦然,新手可以一開始接觸 FORTH 就學生孩子的方法,操作過程也很簡單,耗時並不長,但很可以引起大家的興趣。

在 Win32Forth 系統中,以蛻變編譯器(Meta-compiler)產生新系統的方法:

(1).先由執行中的 Win32Forth 標準系統載入蛻變編譯程式,其程式名稱為 META.F ,由鍵盤鍵入下列指令

FLOAD META.F

系統會自動產生一個核心系統,名稱為『KERNEL應用程式』的可執行檔案。

(2).以 BYE 掉系統的方式,離開現正執行中的 Win32Forth 標準系統,然後去執行前一步驟所新產生出來的 FORTH 可執行檔『KERNEL應用程式』。
此時你已經可以進行一般性的 FORTH 指令操作了,但此核心系統很小,用 WORDS 指令可以看到它所擁有的少數有限指令。

(3).從這個執行中的核心系統載入延伸出整體性能的主控檔案,其程式名稱為 ENTEND.F ,由鍵盤鍵入下列指令

FLOAD EXTEND.F

系統便自行載入所有相關程式,稍等片刻,一個完全像 Win32Forth 的健全新 FORTH 系統,『WIN32FOR應用程式』就會產生出來,另外附帶產生幾個為了應付 XP 作業系統所須要的相關檔案,完成了一次生產。

V6.14版依然使用傳統 FORTH 的蛻變編譯原理來生成系統,也與 V4.2 版的基本原理相同,差別只在更複雜的 V6.14 系統,就有更複雜的 EXTEND.F 內容。

透過生物學的模式,了解 FORTH 蛻變編譯程式的基本原理,是很好的學習方法,學過之後也有助於永久記憶,這是我的經驗。

從上述有別於一般介紹 FORTH 特性的敘述,你可以看出,我以異於常規的方式思考問題,所以也能以異於常規的方式設計系統,『ABC FORTH數學計算系統』就是以這種方式設計出來的。

已經獲得這個系統使用說明的人,用後應該有所體會,這個系統沒能像市售系統那般,提供所有的慣用指令。使用說明的最後一頁告訴大家,設計這個系統的目的並非在重新創作一套程式語言系統,設計它的初衷,只在改善 FORTH 的數學計算應用性能。但是,這個系統絕對可以達到任何數學計算程式要求的程式執行能力,我已舉出大型有用的實例證明了它。

每當我使用這個系統設計程式時,依然難免需要準備好使用說明,隨時參考,才不會用錯指令或函數名稱,這就是使用說明的實際價值。想把所有慣用指令或功能特異的指令都設計出來,我也不是辦不到,但會破壞這個系統的真正價值,我將設計這個系統的眼光看得很遠,試想,五十年後還想讓這個系統容易繼續存在,唯有現在就將其設計成最精簡扼要的程度才有可能。 FORTH 的哲理就是這樣, eFORTH 的精神更是如此, ABC FORTH 也不例外。有 FORTH 先進,建議我將某幾個指令設計成某種格式,將更為理想,我也同意這些構想很好,但我自肘功力絕對不及 Charles H. Moore ,最好暫時別改他的設計,恰如孔子所說的:『爾愛其羊,我愛其禮。』,使用者就請以禮待此系統。

我曾在講演的投影片中列舉了 ABC FORTH 數學計算系統的十大特點:

(1).全面讓不同程度的所有使用者,易於設計數學計算程式。
(2).讓過去幾十年所發展出來的 ALGOL、FORTRAN、BASIC 數學計算程式,易於轉譯改寫進本系統而再度有用。
(3).這個系統易於自行建立,因此可以確保幾十年後這些數學計算程式仍然有用。
(4).改善此前不方便設計數學計算程式的缺點,此後再配合即時性處理數學計算程式的能力,便可以設計出線上立即執行數學計算功能的程式。
(5).讓數學計算程式全面中文化。
(6).讓數學計算程式全面各國語文化。
(7).讓發展出這個系統的技術全面大眾化。
(8).為未來可能出現的新數學體系留下發展典範。
(9).救貧不救懶:對缺乏程式工具與程式技術者有用,對懶得寫程式者不管用。
(10).這是一個永遠免費的公益系統。

在系統公開的初始階段,這十個特點可能要靠我自己身體力行來給予證明,我不怕做這樣的事情,我也樂於從事,因為『ABC FORTH數學計算系統』是我個人的創作,我有恆心與毅力來推廣它的應用。

好的創作是有意義的,在創作中記錄寶貴的經驗就是更有意義的事情,因為對 FORTH 技術的傳承會有幫助。公開好的作品也應該是有目的的,絕對不可以無的放矢,套用一句中華民國到處可見的優良標語:『生命的意義在創造宇宙繼起的生命,生活的目的在增進人類全體的生活。』您是否仔細思考過這個標語的真正意義?這篇文章中談到了生命,『ABC FORTH數學計算系統』是 FORTH 繼起的生命,也談到了目的,這個系統的真正目的就是想增進人類全體的生活。

2026年6月2日 星期二

改變執行秩序的技巧

改變執行秩序的技巧


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 June 2026


剛開始接觸FORTH程式語言時,大家都對這種程式語言整個的特性感到陌生,因為它的使用習慣與所有其它的程式語言完全不同,系統強調數據傳遞時,全面使用堆疊、大部份指令都採用後算符的格式來設計、程式中竟然可以操控系統的執譯(Interpret)或編譯(Compile)狀態變化……等等,許多特性都只在 FORTH 程式語言中才有,乍看之下是不好用。

但若仔細推敲,卻讓使用者感到其執行結果都是直接了當,方便之至。想看記憶體的內容時,就直接飛取(Fetch, @)來看,或直接傾印(DUMP)來看。想改變數值計算的進制時,就直接將任意的進制值存入系統內名為數基(BASE)的變數。甚至於存或取出大批資料儲存媒體龐大硬碟的內容時,也是實體位置的直接操作。具有電腦對外界直接進行輸出或輸入的指令可用。它的許多長處蓋過了它的許多短處。

事實上,所有 FORTH 的短處,在發明當初,都有它被設計成那樣的理由,這些短處是顧及全面性的問題而建立,不是簡單隨便用一用 FORTH 的人所能體會出來的,一個好手要在設計過大型應用系統之後,才能得到這些感覺。相對的,雖然 FORTH 後來發展出許多新技術,但可以感受得出來,為了解決問題而發展的新技術,通常都只有針對性,而未具有絕對的必要性,歸根結底的研究之後,就能發現幾乎可以不必刻意發展某些新技術,例如:後來才發展出來的局部變數(Local variable)設計技術,即為一例。

某些新發展出來的新技術,就應用層面而言,通常也比較狹隘,難稱能夠顧全大局。例如:我為了改善 FORTH 數學計算的程式設計方法,而在 FORTH 基礎系統上,加建了 ABC FORTH 的新性能,這樣做,純粹只是為了數學計算問題而加建,沒有顧及其它如字串處理類的問題,而且根本不修改原基礎 FORTH 系統的任何功能, FORTH 還是 FORTH 。

新技術與新觀念是需要長時間才能發展累積出來的, FORTH 中令人感到原為短處的性能,都能隨著發展而改善,原為長處的性能,也能因發展而更為提升,本文就討論這方面的問題。

大家剛開始研究 FORTH 程式語言的性能時,曾經歸納出一份新手學習曲線圖,圖中表示:一開始就講解如何以冒號 (:) 定義開始設計程式,並以半冒號 (;) 結束定義單一個新指令的程式設計,五分鐘內就能學得FORTH程式設計的最基本方式。接下來就有一點困難了,典型第二級比較難以學會的指令是處理文、數字輸入的 WORD 與 NUMBER 指令。然後,第三級學習上更艱難的指令就是創造(CREATE)與實踐(DOES>),兩者組合而成的特異功能性程式。第四級難懂的多工觀念。最後,也屬第四級最難完全透徹了解的觀念,就是 FORTH 能自行產生標的(Target)系統的蛻變編譯。


指令執行的先後秩序

大概所有認得 FORTH 程式語言的人,都知道後算符是怎麼一回事了。尤其是使用者想完成兩個簡單數字的計算時,算元(Operands)就得先擺在前面,將它們先放在堆疊上,然後才能執行後算符式的算子(Operator)指令,所得結果,仍然留在堆疊上。指令執行的先後秩序,跟其它程式語言都不一樣,有點煩人。

但是,這些限制,實際上都並不是絕對一成不變,非這樣不可的規矩, FORTH 程式語言,完全允許使用者對任何不滿意的狀況,自行設計改善程式來改變狀況。例如:如果不滿意於 FORTH 系統硬要使用者按照後算符的規矩使用四則運算,也可以立即設計程式進行改善,程式也很簡短,啟動系統後,幾乎可以立即建立這樣的功能。程式簡單的程度,就如下列:


 
FORTH  DEFINITIONS

VOCABULARY IKQ   \ Integer Calculator
VOCABULARY FKQ   \ Floating Calculator

ONLY FORTH ALSO IKQ ALSO DEFINITIONS

: NEXTI BL WORD NUMBER DROP ;
: + NEXTI + ;
: - NEXTI - ;
: * NEXTI * ;
: / NEXTI / ;
: = . ;

ONLY FORTH ALSO FKQ ALSO DEFINITIONS

: NEXTF BL WORD COUNT >FLOAT DROP ;
: + NEXTF F+ ;
: - NEXTF F- ;
: * NEXTF F* ;
: / NEXTF F/ ;
: = G. ;

FORTH DEFINITIONS



將上列程式載入系統後,按傳統規矩執行數字的四則運算,就能得到下列直截了當的計算顯示,最後再以宣告 FORTH ,讓系統回到正常字彙之下的方式,將一切恢復成正常的狀態:

IKQ ok
9732 - 123 = 9609 ok
FKQ ok
6.02E23 / 3E0 = 2.006666667E23 ok
FORTH

這樣的四則運算是標準的中算符而不是後算符了,原來的 FORTH 系統卻完好如初,未經破壞。這樣的性能, FORTH 發明當初就已經存在了,因為上述程式中所有用到的指令,在 FORTH 程式語言發明時就已存在。此處藉著這樣的用法,來解釋第二級難度的標準指令 WORD 與 NUMBER 。

首先,必須聲明的是,想要全面實現中算符書寫方式的要求,不能僅只依靠上列的程式來完成。因為,還有許多系統性的問題必須考慮,例如:上述的設計並不顧及先乘除後加減的問題,也不允許任意使用小括弧,也不考慮任何數學函數,也不能用來處理非數字之變數的計算……等等。所以,這樣的性能是具有非常之針對性的,不是一切。

那麼,又是甚麼性能?讓我們能夠迅捷的實現這種針對性的要求。簡而言之,就是透過適合用來改變指令執行秩序的 WORD 指令,予以完成的。

在 FORTH 系統的文、數字輸入處理中,隱性的存在了三個觀念上的緩衝區(Buffers),第一個是終端機輸入緩衝區(Terminal input buffer, TIB),處理直接由鍵盤鍵入的文字,可以直接執行 TIB 指令顯示這個位址。第二個是現行輸入文字緩衝區(Word’s buffer),它就在系統字典長到此處(HERE)的位址處,或稱之為將要建立連結進系統的下一個可用新位址,可以直接執行 HERE 顯示這個位址。第三個是專門用來處理字串,但可隨系統增長而浮動存在的字串暫時儲存緩衝區(Text’s buffer),這個緩衝區的位址,可以直接執行 PAD 指令顯示出來。

古時候的 FORTH 系統,還使用專門用來將輸入字串送進 PAD 緩衝區的指令,它就名叫 -TEXT ,後來的系統不再使用而消失掉了。新作業系統太複雜,而令 FORTH 系統不得不在 TIB 緩衝區,將 FORTH 系統與作業系統進行嚴格的區隔,因此, TIB 緩衝區的一般性操作,也改讓使用者在較高層次的其它高階指令內進行。唯獨這個處於 HERE 緩衝區的 WORD 指令,永遠存在,沒有被除去,只不過某些 FORTH 系統在 WORD 指令執行之後,在堆疊上留或不留下 HERE 的位址,有所差別而已。不被除去的原因也很單純,因為輸入的文、數字,可能立刻就要被編納入系統,所以就理所當然的該留在 HERE 所處的位置。將字串送進記憶體的專門指令中,還有一個專門用來送進任意記憶體位址,而不是這三個緩衝區的 EXPECT 指令,它工作原理簡易,只屬於第一級的初級層次,此處不詳加解釋。

WORD 指令在執行前,必須提供一個當作邊際界標的字元,通常,根據 FORTH 程式語言的習慣,我們都取用空格(Blank, BL)當作界標,隨後, WORD 指令會隨著系統被指定的輸入狀況來處理輸入,輸入有可能只是終端機的螢幕,也可能是來自於一個檔案,或其它能夠被指定的輸入方式。他的處理方式,就是以輸入界標字元為邊界,要求系統處理此單一個輸入字串,處理完則留下 HERE 的位址。系統因此就只處理執行 WORD 指令當時的那一個字。

在上述程式中,面對整數時,系統會把輸入在 HERE 處的字串,經由 NUMBER 指令轉換成數值,放在堆疊上,然後,那個 HERE 位址已經無用了,所以 DROP 掉。面對浮點數時,狀況相類似。如此形成了一個名為 NEXTI 或 NEXTF 可共用的指令,新指令名稱的意思,是準備接受一個後續輸入數字的新指令,這個指令供新的四則運算指令,共同用來設計新的執行內容。例如:加法 (+) 指令,在新的規格中,被定義成先別急著開始直接執行加法,而是透過 NEXTI 或 NEXTF 的要求,先處理一個後續的輸入字串,它必須是一個能被 NUMBER 轉換得成的整數數字,或必須是由 COUNT 、 >FLOAT 兩者組合而成,能將輸入轉換成浮點數的字串,轉換出來的數字均放置到指定堆疊上之後,才執行原本的加法運算。

如此一來,系統碰到加 (+) 指令時,就不會直接加,而是到 + 的後面去處理出一個數字才執行加法了。其它的所有四則運算指令,都被以同一方式,定義成新的執行格式,所以就完成了能夠執行中算符式的四則運算程式設計。

為了使用系統中已經用掉了的同名同功能指令作為名稱,我們藉著字彙的宣告,來區分出彼此的不同。因此,在這樣子使用四則運算時,必須先行宣告使用 IKQ 或 FKQ ,用完了也不可忘了使用宣告 FORTH ,讓整個系統恢復正常。

WORD 指令經常在想要改變單一個指令的執行秩序時,用來設計程式,不單只是處理數字時可以使用,處理文字字串也經常使用。例如:我們想改變檔案處理指令的執行秩序時,不希望檔案的全名必須放在相關操作指令的前面,而希望可以改放在後面,就如同 FLOAD 那樣的使用方式時, WORD 指令就又能派上用場。這類型的使用範例,在 FORTH 程式設計環境中幾乎是屢見不鮮,因此,使用者應該用心了解並活用 WORD 指令的實質功能。我在設計 ABC FORTH 數學計算系統時,當然也用過這些設計。

改變單一個指令的執行秩序,是藉由 WORD 指令的功能來完成;程式的執行秩序,也發展出可以改變先後秩序的技術了。

程式設計的先後秩序

這一次我們不談傳統的由下而上(Bottom-up)程式設計規矩,改談弄亂(Mess up)這種設計規矩的方法。

我在每篇網文中或多或少的都提供給讀者一些 FORTH 的相關技術,希望它們能夠傳承下去,我的目標是想將技術內容寫到最簡單的程度,要人人能看得懂,每篇文章扯到 FORTH 的內容實在不多,觀念又淺顯,所以應該很好學也很容易學,重點是您自己得動手實作設計程式,才會有真正的結果,光看文章是永遠看不出名堂的,這件事我要不斷的提醒大家。

最近在與 FORTH 相關的國際交談網站中瀏覽時,見到有人開始談論許多非英文的環境,出現了各種用 FORTH 創作的新系統。一般的意見我就不提了,其中兩項很突出的言論是:他們看不懂非英文的技術說明,但是覺得系統的性能非常傑出。另外一個突出意見就是: FORTH 的學習曲線非常陡峭(Steep learning curve),他們也同樣感受到 FORTH 的未來發展越來越艱辛。

見到這樣的言論,一則以喜,一則以憂。喜的是:講英文的人終於搞清楚了,他們不再獨霸技術,一生光講英文不學第二國語文就要不如人了,而我們一生辛苦的苦讀中文還得學英文,終於還是得到了許多好處。憂的是:陡峭的 FORTH 學習曲線普及全世界,對講那一種語言而言,統統一樣, FORTH 的確是難學!

不患人之不己知,患不知人也。只講英文的人,看不看得懂我們的技術?實在不重要,但也不可沾沾自喜,設法全面了解他們的所有技術,才是正途。

基本的 FORTH 程式設計原理,是用冒號『:』及半冒號『;』來定義出一個新指令,然後強調:『前面設計好的指令,後面的程式才能拿來用』,亦即『先造後用』,也就是說,程式設計的規矩為由下而上(Bottom-up),最後就完成了整個程式。會 FORTH 的人都知道這幾句話是老調重彈,此處則引用它來產生新話題。古時候最早期的一般 FORTH 系統確實是只能這樣用的,後來就發展出可以不按這個規矩設計程式了。中華民國已經出版過的 FORTH 基本教材,沒談或來不及談這個主題,所以就值得在這裡談。

天下事都是相對的,有大就有小(所以不要老是愛跟別人比較大或小),有高就有低(所以不要老是愛批評別人高或低),設計程式時要由下而上,思考要如何設計時,就得由上而下(Top-down)了。因此, FORTH 發展到後來,就有一些調皮工程師,設法打破這種規矩,想出了一些弄亂規矩的方法,創造實現出了新技巧。技術一直都在演進,新技巧方便、好用就被留下來,反之,就算新技巧能用 FORTH 自身設計出來,也不太有人想用,久了就被世人所淡忘。

其實,本網頁網文已公開的程式中,已經可以找到許多如此設計程式的例子使用了 DEFER 這個指令。想用來設計可以執行出任何一列數學式子的多功能性指令時, DEFER 就具有我們所談論主題的特性。它被擺置在程式的很前面,甚至於可以擺在最前面,使用當時,還不知道它被執行內容的影子到底在那裡?指令的這種性能,幾十年前,我們曾在中山科學研究院的週五夜間 FORTH 研討會上,給了它一個很恰當的中文術語,稱為:『先用後造』,或稱為『未造先用』,本文暫時統一稱之為『先用後造』。有別於『先造後用』的 FORTH 傳統程式設計規矩。

DEFER 指令不是伴隨原始 FORTH 系統而存在的指令,隨便舉一個例子,如早期的 MVP-FORTH 系統就沒有,它也沒被列入 FORTH-83 標準,所以很多早期的 FORTH 基本教材不教這個指令,它現在已經是 ANSI 標準指令之一了。

莫管 DEFER 指令的英翻中意思為何?它最恰當的指令中文譯名應該為:(宣告一個可以儲存起始執行位址用的)『向量』(Execution vector),如此,它便可以順理成章的與常數(CONSTANT)、變數(VARIABLE)、字彙(VOCABULARY)、變常數(VALUE)…等同類指令擺在一起,它們都是想宣告出某種資料結構時,必須使用的指令。

DEFER 的字典意思為『延緩』、『展期』,都不適合拿來當作指令名稱,但經其宣告而成的後續指令,具有這些意義,它表示宣告出一個可變向量名稱,並且可以延緩設計了內容後,才將起始執行位址存入此名稱,簡言之,就是可存執行位址的向量, DEFER 沒有動詞的動作,卻像變數宣告,只有宣告出名詞的意義,因此,就簡稱『向量』最為妥當。

DEFER 很好用,它也不單純的只是擁有『先用後造』性質而已,您若回頭去看看我在前面文章中的使用範例,您就可以體會出,業經 DEFER 宣告過的執行向量指令,可以有千變萬化的執行內容,只要系統的記憶體容納得下,任何在其後面設計出來的指令,管它們是否風馬牛不相及?全都可以當作它的執行內容,所以應該命名為執行『向量』。變動執行向量內容的方法,也簡單到只需使用『’』(遞給)與『IS』(是)兩個指令,便可達到目的,形如下式:

’ [new-name>] IS [deferred-name]

因此,前文的例子,只需一個程式,就可以讓讀者繪出所有合理的函數曲線。它後來才被發明出來,因為好用,就被納入 ANSI FORTH 標準。

我在設計ABC FORTH 數學計算系統時,發展到達最後階段,感覺數目字的輸入格式被系統限制住了,非常不滿意,仔細的研究過整個系統之後,發現修改核心指令 NUMBER 的執行內容,才能根本解決這個問題,而大部份 FORTH 系統都將 NUMBER 的設計內容固定下來,不允許使用者隨意更改,我得依靠執行向量的觀念才能對 NUMBER 指令的執行內容進行修正。解決的辦法就是在系統源程式的 NUMBER 指令前面,宣告出一個執行向量。

大部份國外設計出來的 FORTH 系統,都不管能否在系統中自由使用中文?的問題,想修改這些限制,通常必須修改系統指令名稱欄內的結構,而系統原本的性質還不容許被破壞,解決辦法也是到源程式中找到關鍵指令,將其內容改成執行向量的附屬內容之一,隨後就可以為所欲為的設計出適合中文環境的系統來。但對後來的大型 FORTH 系統而言,此項工程通常有點浩大,使用 FORTH 的能力,未達某種程度的人,不易辦到,進行這種工作也不輕鬆,我自知,我不行。

關於在 FORTH 系統中使用中文的問題,我們有一些經驗,分述如下:

中華民國有不少人曾在 Win32Forth 系統上下過功夫,它可以順利使用中文。
易符公司發行的 F# FORTH 系統則先行考慮了容納中文。
Forth Inc. 發行的 Swift Forth 則無此功能。
OS/2 DPMI FORTH32 系統,在 XP 環境可以執行,但也不能使用中文。

上列後面的這兩個系統,都是當初在開啟其自身所需要的一個顯示視窗時,沒顧及這項要求所致,因牽扯到如何將 FORTH 系統架設在作業系統上的技術,使用者必須從作業系統那方面重新搞起,通常難以修正。

可由 C 編譯而成的 gForth ,通常只藉一個視窗來當作系統的終端機顯示器,所以能顯示中文就能用中文,但指令名稱字長限定為少於 31 個位元組(Bytes),所以中文指令用名不可以太長。

其他的系統我未試用,啟動系統後直接試用,就知結果。

FORTH 是全面透通式的程式語言,透通到能讓使用者在系統內為所欲為,但『後果自行負責』,軍中帶兵的長官下達命令時,常用這一句話。古時候,我們想完成類似 DEFER 指令的功能時,人工操作遞給『’』指令得到 CFA 執行位址,再用 DUMP 指令仔細看可以替換的位置在那裡?配合設計一個變數,進行直接存取更換,也能達到同樣的效果,但操作起來實在太累了,所以 DEFER 是個解決問題的好指令,應該存在。

突破必須先造後用傳統程式設計規矩的指令,不只 DEFER 一個而已,也是後來才出現的 ANSI 標準的另外兩個指令: RECURSE 及 EVALUATE 也是。

EVALUATE 性能與 DEFER 有延伸性關係。比較二者,則 DEFER 形同是只能儲存單一個執行位址的向量, EVALUATE 則形同是可以執行掉被儲存之一長串執行位址的陣列,而且也是在面對必須先用後造的情況時才使用,下列的精簡測試實例,能夠協助您自然了解它的用法,不需要太多文字說明。

: DO-NOT-YET ( - - )
S” DONE1 DONE2 DONE3 ” EVALUATE ;
: DONE1 CR .” ONE DONE . ” ;
: DONE2 CR .” TWO DONE . ” ;
: DONE3 CR .” ALL DONE . ” ;

這個範例程式展示 DO-NOT-YET 指令先用後造了 DONE1、DONE2、DONE3 三個指令,它靠著標準指令 EVALUATE 以及 S”…………” (這是一組就地形成字串的指令)達到目的。因此,此後執行 DO-NOT-YET 指令時就會顯示出這樣的效果。那麼,如果 DEFER 譯成執行的『向量』, EVALUATE 就該譯成『執行字串』了,意思是執行掉前面的一連串字串陣列,它有動詞的動作(執行掉),而不是宣告出名詞。如果沒有 EVALUATE ,就可能需要使用好幾個 DEFER 才能達到目的。只不過這樣安排 EVALUATE 的指令性能,使它無法像 DEFER 一樣也可以隨時更換執行內容而已,將來若有需要,它的格式自然也就會發展成此處所描述而尚未具有的性能。

若再舉一反三推演下去,也可以設計出一個『執行矩陣』來, DEFER 是宣告出一個點, EVALUATE 是執行掉一條線,那個不知名的執行矩陣就是一個面式結構,處理 3D 的執行向量對 FORTH 而言也不是問題,接下來就不描述了。

上述造來造去、用來用去的關係也能用來解釋一個『現造現用』的指令,就是 RECURSE ,先讓我們舉例說明後,再強調它應有的適當中文譯名。

古時候的其他程式語言,例如:FORTRAN,通常稱這種指令性能為自用副程式,性能有一點特別,有些程式語言也不允許使用者如此設計程式。在我們的 FORTH 中,它的意義是『將尚未定義完成的自己納入定義』,常有人舉階乘(Factorial,例如:數學表示式為 5 ! = 5 * 4 * 3 * 2 * 1) 的計算為例,使用自用副程式的方式來完成設計。此處另舉一個反過來往上印出數字的例子,增加您對此性能的認識。程式與實際執行結果,轉錄如下:



 

\ Nursery rhyme : counting hoptoads
\ 四川民謠:數蛤蟆。
\ 作者:曾慶潭,2010年4月2日於紐西蘭,版權所有。
\ Author: Ching-Tang Tseng, 2Apr2010, All rights reserved.
\ http://forthfortnight.blogspot.com
\ ilikeforth@gmail.com

10  VALUE  LIMIT

: READY  2  ;
: GO
  CR
  DUP . ." toads, "
  DUP . ." mouths, "
  DUP 2 * . ." eyes, "
  DUP 4 * . ." legs. "
  ." Hoppity hop, hopped into the pond."
  1+ DUP LIMIT >
     IF DROP EXIT
     ELSE RECURSE
     THEN ;
: ARE-WE-GOOD? ;
: SING1 ARE-WE-GOOD? READY GO ;

: 預備  1  ;
: 唱
  CR
  DUP . ." 隻青蛙 "
  DUP . ." 張嘴,"
  DUP 2 * . ." 個眼睛 "
  DUP 4 * . ." 條腿,"
  ." 噗通!噗通!跳下水。 "
  1+ DUP LIMIT >
     IF DROP EXIT
     ELSE RECURSE
     THEN ;
: 準備好了嗎? ;
: 數蛤蟆  準備好了嗎?  預備  唱  ;
: SING2   數蛤蟆  ;

FLOAD RHYME.F
From file: RHYME.F word: GO isn't unique  ok
SING1
2 toads, 2 mouths, 4 eyes, 8 legs. Hoppity hop, hopped into the pond.
3 toads, 3 mouths, 6 eyes, 12 legs. Hoppity hop, hopped into the pond.
4 toads, 4 mouths, 8 eyes, 16 legs. Hoppity hop, hopped into the pond.
5 toads, 5 mouths, 10 eyes, 20 legs. Hoppity hop, hopped into the pond.
6 toads, 6 mouths, 12 eyes, 24 legs. Hoppity hop, hopped into the pond.
7 toads, 7 mouths, 14 eyes, 28 legs. Hoppity hop, hopped into the pond.
8 toads, 8 mouths, 16 eyes, 32 legs. Hoppity hop, hopped into the pond.
9 toads, 9 mouths, 18 eyes, 36 legs. Hoppity hop, hopped into the pond.
10 toads, 10 mouths, 20 eyes, 40 legs. Hoppity hop, hopped into the pond. Ok
SING2
1 隻青蛙 1 張嘴,2 個眼睛 4 條腿,噗通!噗通!跳下水。
2 隻青蛙 2 張嘴,4 個眼睛 8 條腿,噗通!噗通!跳下水。
3 隻青蛙 3 張嘴,6 個眼睛 12 條腿,噗通!噗通!跳下水。
4 隻青蛙 4 張嘴,8 個眼睛 16 條腿,噗通!噗通!跳下水。
5 隻青蛙 5 張嘴,10 個眼睛 20 條腿,噗通!噗通!跳下水。
6 隻青蛙 6 張嘴,12 個眼睛 24 條腿,噗通!噗通!跳下水。
7 隻青蛙 7 張嘴,14 個眼睛 28 條腿,噗通!噗通!跳下水。
8 隻青蛙 8 張嘴,16 個眼睛 32 條腿,噗通!噗通!跳下水。
9 隻青蛙 9 張嘴,18 個眼睛 36 條腿,噗通!噗通!跳下水。
10 隻青蛙 10 張嘴,20 個眼睛 40 條腿,噗通!噗通!跳下水。  Ok

請仔細閱讀並實際試用這個程式,它具有多方面的用途。

至少看看中文後再欣賞一下英文,程式裡面的文字,純粹是我個人的創意作品,不認識的單字就請查一下字典,我小女兒協助我在裡面使用了一個無意義的協助發音單字,字典上沒有。

這是一首童謠詩,全中華民國的同胞都熟悉,但您可能不知道童謠文學可以如此與程式科技結合,電腦一路唱下去,可以正確的算出好多條腿。

這樣的童謠還能幫助我們測試系統的回返堆疊容量到底有多少?人們在唱這首童謠時,唱錯的人就得停下來接受處罰,然後換人再唱,不是嗎?您不要以為電腦依此程式一路唱下去,就不會唱錯了,您可以經由更改 LIMIT 的設定值來進行這樣的測試,例如:直接執行 1000 TO LIMIT 。

能使系統算錯了腿數的設定值,就是這個系統回返堆疊的單位容量(Cells capacity),系統立刻執行例外處理。Win32Forth 為 1985 , gForth為 65257 ,出錯時系統都還可以健在,只是不再繼續執行程式。 F# Forth 回返堆疊的容量則很大,我無暇仔細測試,但知道十六進制的 40000 可使系統崩潰,您得重新啟動 F# 後才能再試。

各種 FORTH 系統的回返堆疊容量有這麼大的差異,沒甚麼好奇怪的,它端賴於設計者的系統原始規劃,通常容量也是可調的,只是您得自知調整方法。容量的大小也不代表系統的好壞, F# Forth 啟動後的新鮮狀況,能讓回返堆疊容量這麼大,判斷它係被規劃成開放式的成長空間所致,當程式跑到回返堆疊要用到系統自身的記憶體位置時,系統就崩潰了。 Win32Forth 及 gForth 的回返堆疊空間雖較小,系統卻進行了妥善的處理而不會崩潰。再說, Charles Moore 親自設計的 FORTH CPU i21 ,它的回返堆疊只有 17 個單位,設計人是 FORTH 程式語言的發明人,您能說 i21 不好嗎?

上述程式中使用 RECURSE 的位置有特別意義,它的功能為:就在這個位置填入當時指令(『GO』或『唱』)自身的執行位址,但當時系統尚未完成該指令的編譯,本不允許,例如:您直接將 RECURSE 改成 GO ,就無法通過系統的編譯(但我曾用過可以這樣設計程式的 FORTH 系統,忘了是那一個了), RECURSE 則實現了這樣的要求。了解了它的性能,再配合上述同類指令的譯名, RECURSE 因確實是在做現造現用的工作,它不是宣告性指令,卻像是動詞式的實際動作性指令,執行填入起始執行位址碼於此處的動作,所以應該被譯成『叫用自己』,您說呢?

FORTH 中使用的自用副程式概念,與其他程式語言所用者,在觀念上有點不同。 FORTH 指令間參數的傳遞只靠堆疊,但其他程式語言,次程式傳遞廣泛的資料,凡包裹在小括弧內者均是,彼此不同。除非參數只有少數幾個,否則想要轉譯別種語言的程式成 FORTH 程式時,會感到很不方便。我在使用 ABC FORTH 數學計算系統進行程式轉譯工作時,有此強烈的感覺。

自用副程式的使用要求,係必須在程式中設計了正確的中止執行條件,否則會令系統循環不已。上例中,數字大於 LIMIT 時就會中止,所以執行起來沒有問題,另外,這個可以執行自己的指令,會將自己的 CFA 執行位址不斷的推擠進系統的回返堆疊,如果叫用自己的層次次數太多,就會用光回返堆疊的結構,此一不良情況,您也得自行負責,在 Win32Forth 及 gForth 系統中,將此狀況處理成:用完回返堆疊就算了,執行會自行中止,系統並不會崩潰,所以我們才能利用這種特性來測試回返堆疊的設計容量,您讀了本文,應該自己去試一試。

至於程式設計時偶爾也會用到的性能類似指令,另有:『造了無用』,通常卻『另有妙用』的無執行內容指令NOOP。還有:『造了不給別人用』,『光給自己用一次』的 :NONAME 指令。 NOOP 係非標準指令, :NONAME才是 ,指令功能請參考標準指令之相關說明,想要熟悉它們的用法,您得參考許多高手設計的程式後,才能體會出它們的真正意義,我已見過許多,也在一些文章中稱讚那樣的用法。當然,最糟的情況就是『不造不用』、您千萬別當一個『不用』又『不造』的 FORTH 使用者。

本文大膽的開始給予論及的 FORTH 標準指令中文譯名,但譯名全都其來有自,絕不亂譯,傳承 FORTH 技術時,需要中文譯名,譯其名者也應有縱觀天下的眼光,而且要符合時代潮流,我歡迎能有如此素養的同好共同探討。

三個論述的 ANSI 標準指令中文譯名,再度摘要如下:

DEFER 向量
EVALUATE 執行字串
RECURSE 叫用自己

搞通程式設計的先後秩序,是現代 FORTH 較為強調的重點,才會有上述這些指令的加入系統現象,本文回溯古典 FORTH 的歷史,可讓讀者更容易了解新標準指令的性能,講解的方法採取了『四書』首書『大學』第一章,開宗明義的醒世名言:『物有本末,事有終始,知所先後,則近道矣。』


2026年5月16日 星期六

編寫與執行 FORTH 程式方式的歷史回顧

編寫與執行 FORTH 程式方式的歷史回顧


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
16 May 2026

無法適應編寫與執行程式的操作環境,是正式使用 FORTH 系統前的第一大障礙。

2011 年時,流行之 FORTH 系統的使用方式為:

編寫程式時:強調使用在執行中的 FORTH 系統外面另開編輯視窗,進行檔案式編輯。也強調可以配合滑鼠來操作。

執行程式時:繼續保留在執行中的 FORTH 系統視窗內,直接輸入『載入檔案』(FLOAD 或 INCLUDE)指令式的傳統執行操作功能。但更強調從執行中的 FORTH 系統視窗上緣工具列內,操作滑鼠,選用『檔案』(File)一欄,拉下菜單式選項,由其中選擇『載入檔案程式』(Load Forth File)方式來執行。

傳統與當時流行的編寫或執行程式之操作方式大大的不同,但都有存在的價值,不可偏廢,本文就細述大家可能見到的操作情況,使用者了解之後,便能夠適應於各種情況,然後順利的使用系統。

程式編輯器概述

所有的電腦程式語言,在發展階段都需要編寫程式,編寫程式的方式也不斷的在發展,了解並習慣於程式編寫操作,就成了正式使用一個程式語言系統之前,首應學會之事。程式編寫環境確實有著優劣之分,而且關係到程式隨後被執行操作時的便利性。程式編寫功能,完全建立在系統的編輯器(Editor)上,它也是一個被設計出來的程式,欲設計出健全優秀的功能,必須在編輯器程式上付出設計代價。現代大型的程式語言系統,可以不考慮執行速度與記憶體容量的問題,因此,對編輯器功能的要求,當然是越優秀越好,編輯器程式就會被設計得非常龐大、複雜,而且僅只考慮使用時的友善性,令其好用與方便。

FORTH 程式語言,其程式編輯器的設計要求,大體上與傳統程式語言相同,但由於語言特質不同於一般程式語言,有時, FORTH 的程式編輯器還反過來不求方便,而將發展重點集中於自身系統性能的積極改進上,甚至於根本不設計編輯器,而改採別人提供現成可用的編輯器。理由無它,僅因 FORTH 系統太容易自行建立,而自建於作業系統中時,因作業系統願意公開的資源有限,妨礙編輯器的設計,於是延伸出許多FORTH系統不太在乎編輯器性能是否優良?卻照樣被愛用者高度接納的不合理現象。

為了適應流行的趨勢,歷代暢行的 FORTH 系統,亦均有其符合當時趨勢的優秀程式編輯器,還隨時都不斷有人為當時編輯器進行性能改善設計。換句話說,好的編輯器都不是一次性完成的產品,此一現象也與一般程式語言的狀況不同, FORTH 系統一切透通,這樣子發展也很自然。 2011 年流行的方式,是在 FORTH 系統操作視窗環境之外,另開視窗編寫程式,其中功能卻不斷更新,但主要功能只有幾項,仍然容易掌握。將來的編輯器必然與現行者不同,根據以往的歷史, FORTH 不會在乎,也不需要在乎別人的發展, FORTH 完全可以配合時代潮流來演進,因為系統的任何部份都很容易自建。

一般程式語言系統的編輯器,通常是與系統主體可以明確隔離開來的,也就是說,編輯程式時,就只編寫程式,不做其他事情;編譯程式時,就只做編碼之事,也不再涉及編輯之事。 FORTH 不同,所有系統內的任務都可以混著執行,編輯器也與系統主體完全連在一起,因此,程式編寫時就也同時考慮到隨後要交給系統執行之事;系統執行程式時不排除可以執行涉及編輯器的任何事情,甚至於可以共用資源或交互支援,這一方面, FORTH 與一般程式語言不同。

編輯器的演進歷史

第一套被設計出來的 FORTH 系統,可能沒有也不需要編輯器,因為 FORTH 是即時性控制(Real-time control)程式語言,系統啟動之後,直接輸入已經存在的指令,就可立即得到執行結果,所以根本可以不用編寫程式用的編輯器。

一直到今天,全世界仍有大量的使用者喜歡為單晶片電腦加裝自己設計的簡易 FORTH 系統,系統可以執行出結果後,為了能夠方便的編寫可以留存的程式,自行設計一個程式編輯器就不可或免,編輯器功能多,編輯器程式就不可能很小,因此,功能恰到好處的編輯器程式反成了值得永遠留存的設計作品。

最早的 FORTH 教材,介紹列式編輯器(Line editor)。使用者可以使用幾個簡單的單個字母,操控編寫出來的程式,將其留存在指定的記憶體緩衝區,感覺上性能相當粗糙,但是,您在自行發展自用系統的初期,簡易編輯器就能滿足編輯需要,還大量節省了記憶體的使用量。編輯器通常不會是您想發展出應用系統的唯一目的,換句話說,最精簡的列式編輯器,永遠都有其存在的價值。如果您的確具有想要自己發展 FORTH 系統之念頭,自行保留一份先聖先賢留傳下來的優良列式編輯器源程式,是必要的。

古早以前的電腦,顯示裝置很簡單,只有一列 20 或 40 個字元,能夠上下滾動顯示的緩衝量也只能三或四列。 隨著時代的進展,電腦系統的輸出顯示,演變成現今的螢幕之後,編輯器也改採以幕式編輯器(Screen editor)為主了。囿於傳統的 FORTH 系統曾將大批儲存(Mass storage)媒體,例如:磁碟,規劃成記憶體用量,每 1K (1024 bytes) 為一個區塊(Block)來操作,較早期的幕式編輯器所設計的編寫程式範圍,也因此以每次顯示、讀、寫 1K 的程式量為主。那時,游標的控制、訊息的插入、刪除、複製、貼上…等各種功能也都陸續被設計出來,好的程式編輯器,確實可以大量節省了使用者發展程式的時間,在電腦系統內記憶體還不便宜也無法大量使用的時代,編輯器佔用記憶體的數量卻成了另外必須考慮的問題,設計者只好在性能與環境適應性上進行均衡取捨。

嚴格說來,幕式編輯器依然延用至今,但細部分析時,幕式編輯器又進行過幾次明顯不同的演進。各個階段主要的差異,起因於後來電腦作業系統,對儲存資料的方式,採用了檔案(File)的觀念,與 FORTH 傳統的區塊觀念不同,編輯器的設計只好隨之而變。

區塊式檔案是一種 FORTH 獨有的檔案規劃方式,這種觀念一直延用到今天,仍然可以發現它的存在,道理無它, FORTH 就是可以很容易獨立存在於任何電腦中,可以完全不依靠作業系統,還喜歡直接對一切硬體的實體位址進行存取操作,以 FORTH 自己發展自己時,就強調這種區塊式檔案的規劃比其他方式都好。使用 FORTH 的能力到達能夠獨立進行發展的程度前,使用者必然早就熟悉各種程式編輯器的操作了,所以區塊式檔案編輯系統的操作說明,實不必特別強調說明,需要使用的人,通常有能力自行了解。

現行檔案式檔案是目前流行作業系統中採用的主流檔案規劃觀念, FORTH 在由區塊式檔案過渡到現行一般檔案式檔案規劃期間,也曾有過臨時變通式的安排。當時,曾採用過以一個流行檔案來包括固定數量區塊的辦法,讓檔案中有區塊,後來,才逐漸的讓區塊式觀念從現行檔案格式中自然消失掉,大部份現行 FORTH 系統,都已經與一般檔案觀念同步,目前,已經罕見仍採用區塊觀念設計的系統。

當 FORTH 的程式編輯器發展到與一般文書處理系統的操作結果相容時,對 FORTH 系統而言,會衍生出許多不能自由載入檔案讓程式進入系統的問題。文書處理系統只強調文件編排,為了美觀與多樣性的功能,所產生的檔案必須包括基本文件所需要的格式碼,這些東西不是 FORTH 系統能夠接受的程式,因此, FORTH 系統不能任意載入一般文書處理系統所編輯產生的 FORTH 程式。

就目前流行的微軟作業系統常用的文書編輯系統而言, Word 編輯器的產品不能直接供 FORTH 系統使用,因其最簡檔案的檔頭內,幾乎有 2K 的記憶體容量,放的是格式資訊。 WordPad 編輯器的產品也不能直接供 FORTH 系統使用,因其最簡檔案的內容,每一列結尾處都安排了特殊的跳列格式碼。只有號稱最簡單的記事本(NotePad)編輯器的產品沒有問題,這是一個幾乎可以稱作完全素文字式的文書編輯器, FORTH 系統就需要與記事本文書處理系統相同的程式編輯器功能,用記事本編輯出來的源程式,完全可以被 FORTH 系統接受。

如果只是單純的編寫程式,現行 FORTH 系統就不需要另行設計自己的程式編輯器了,因為使用系統的記事本可以解決編寫程式的問題。前曾述及, FORTH 系統絕對可以在編輯程式與系統執行狀況之間進行功能串聯,於是, FORTH 系統便仍然自行設計出自己的程式編輯器,就算編輯器的操作功能只等同於記事本,也仍然要這樣做,尤其在強調載入程式期間的輔助除錯顯示功能時,只能靠 FORTH 系統與自己設計出來的程式編輯器直接串聯才容易辦到。

許多電腦使用上的新觀念,也影響程式編輯器功能上的發展,例如:捲動式視窗顯示, FORTH 系統的編輯器也已採用了。滑鼠的操作功能也不例外,現行的 FORTH 系統還很強調必須配合滑鼠的操作,才能毫無問題、快速完成載入程式與執行的操作。例如:編輯產生的程式檔案名稱不是例行顯示文字時,難以單靠鍵盤操作直接輸入檔名,不用滑鼠點選便不能載入系統。另外,因滑鼠操作具有反白選擇、複製、再貼上的功能,有許多狀況,不用這種方式操作就無法將程式載入系統。直接操作滑鼠,當然也比逐個字元的打字操作,要快太多了。因此,現行的 FORTH 編輯操作不能免除滑鼠的功能,並且必須特別加以活用,才能節省程式發展的時間。

時代的進步不會中止,未來的電腦系統,必然不斷出現大眾認為方便的系統操作功能, FORTH 不能自外於世界發展,必須跟進,所以,可以預卜, FORTH 自己的程式編輯器,還得隨著時代的演進而發展。最近,電腦使用觸控螢幕,有大量增加的趨勢,隨後, FORTH 程式編輯器的新設計,可能就會受到影響,這種演變絕對不會停止。但是,程式編輯仍然有其絕對基本的單純觀念可以依循,使用者只是需要花一些時間來適應系統要求的操作,要學好 FORTH ,這就是一件非先行學會不可的習慣性操作。

編輯器的演變歷史:

第一套 FORTH 係直接操作系統,不需要編輯器

一列式編輯器

一幕塊式編輯器

一幕檔案式編輯器

視窗配合滑鼠操作檔案式編輯器

可能為多元控制或非現行檔案格式之編輯器
(配合觸控、聲控、圖示、輸入影像、腦波意念式控制……等多元操作)

.......... (作者也無法預卜之方式的編輯器)


典型的程式編輯器

以 Win32Forth 系統為範例來說明,比較容易詳細了解典型 FORTH 程式編輯器的整體性能。

中文顯示現在已經成為正式電腦的固有性能之一了, FORTH 系統的發展則尚難完全跟進,這也是為什麼我選擇採用 Win32Forth 系統來介紹程式編輯器的原因之一,這個系統配合中文的要求性能,已經完全在我們的掌握中,全世界現在流行的大型 FORTH 系統,還有許多個系統的性能無法達到適應中文的要求。

Win32Forth 系統經過十幾年的演進,陸續出現過許多版本, 2011 年的版本為 V6.14 版,也有其新版編輯器。系統的發展歷史,不是此處打算介紹程式編輯器時的重點,故略而不談,只談單一個有用的編輯器程式。

為了迎頭趕上最新的版本,並且涵蓋整個 Win32Forth 新舊系統均可使用的程式編輯器要求,只介紹系統中最有彈性功能的程式編輯器 WinEd ,但並不需要逐項仔細的介紹出這個編輯器的所有功能,只挑選實際編寫程式時,足夠用來完成工作的操作予以說明,未介紹到的其他功能,讀者習慣於使用這個系統後,可以自行透過試用的方式來了解。

此系統中另有許多其他設計者提供的程式編輯器,若單純只以中文顯示為著眼點來考慮,只有 WinEd 的性能較為健全,也較為精簡,我們就以利用 WinEd 編輯程式,編寫出最簡單的一列迴響印出文字的程式,當作精簡範例,作為開始,並刻意以中文作為檔案名稱,再以滑鼠操作載入檔案,實現整個精簡過程。一般的大型程式編寫操作方法與此雷同,熟悉這個精簡操作過程,就足夠用來設計一般程式。

隨後,再根據作者長期使用此系統的經驗,將逐步介紹其他在編輯程式時,必須妥善利用的特殊操作,以便學會遇到特殊情況時,解決某些困擾的辦法。程式編輯器的所有功能,都不是必然的設計,是許多人使用過後,再經不斷改進設計的結果。另外,包括融入中文或非英文的其他文字,儲存成一個 FORTH 系統能夠單獨識別出來的檔案格式及其圖示(Icon),滑鼠、觸控、色彩、聲效 ..... 等的功能,都必須配合基礎作業系統來設計,背景作業系統缺乏這些功能前,編輯程式也就不便單獨為其建立這些功能。

就像啟動 Win32Forth 系統的方式一樣,啟動 WinEd 時,也是利用滑鼠,在螢幕上那一片以綠葉子圖示的程式編輯器位置,直接點選兩次,系統立刻進入可以編寫程式的新視窗環境。

一開始,我們可以完全忽略視窗中所有邊框的顯示,直接找到螢幕上閃動游標所在的位置,立刻開始編寫程式,例如:就鍵入下列一列程式

: hi .” Hello ! How is going? ” ;

表示我們設計了一個名稱為 hi 的新指令,它的工作就是拓印( .” ..... ” )出一列文字,此處為『 Hello ! How is going? 』,然後就結束了工作,它就足夠代表一個完整的程式了。

接下來的操作,雖不是標準 FORTH 系統所強調的東西,卻是能不能達到讓 FORTH 系統順利載入這個檔案程式的關鍵,為了此後能快速操作系統而節省發展程式的時間,使用者必須熟悉這些操作且養成操作習慣,作者於發展大型應用系統期間,強調曾經一日之內反覆進行了成千上百次這樣的操作,一點都不為過。

WinEd 非常強調藉由滑鼠來完成工作,因此,要存住這個剛編寫完成的程式時,最好利用滑鼠。雖然壓鍵盤的 Ctrl+S 也能達到目的,但可以不用去記憶這樣的操作,像一般文字編輯系統一樣,這種壓鍵操作的功能,大概只在滑鼠功能失效時還有點用途。

WinEd 也採用自上方主要功能分類指示文字中,點選一類,以取得此一大類菜單式子類分支功能選項的設計。從文字說明式的菜單選項挑選一項,操作滑鼠滑動到此一選項,則此被選之項的文字將反白顯示,再以滑鼠左鍵於此位置點選一次,系統便按功能要求執行指定的程式。

第一個要學的操作是存起新建檔案:先點選左上角的『 File 』(檔案)大類,然後在菜單選項中點選『 Save File 』(存檔),因為這是一個新編寫的程式,所以才這樣選擇。

編寫一個原就存在於系統的檔案程式,則係經由 WinEd 以『 File 』『 Open File 』(開檔)操作,自另外顯示的小視窗中找到想要編寫的檔案程式,開啟此檔案程式,編或改寫完成之後,存檔的操作則為『 File 』『 Save All Changed Files 』(存起所有業經改動過的檔案)。在程式發展過程中,這樣的操作,比上述建新檔案的操作更為頻繁。

此處打算儲存起來的檔案,稱為文字程式檔案(Text file),有別於可被電腦系統直接執行的系統程式檔案(System program file),也有別於單純用來儲存資料而非程式,但也是文字形式的資料檔案(Data file),現行電腦系統,也將一個可以包括上述所有型式檔案的資料夾,視同為一個檔案,它的名稱就叫作資料夾檔案(Directory file),它們都是一般常用檔案之各別的名稱。

當使用者進行了上述存檔操作之後,電腦系統會在螢幕上顯現出另外一個小視窗,讓使用者安排打算儲存檔案的去處,這是使用者必須學會的第二項重要操作,熟悉這個操作,並建立實質觀念,此後才能明確的知道,自己設計出來的程式,是以甚麼指定格式?儲存在電腦內的甚麼地方?

尚未建議使用者在 Win32Forth 系統中新建自己專用的資料夾前,此處的操作範例可將新編成的程式檔案,暫時儲存在與 Win32Forth 系統相同的資料夾之下,以便容易進行後續操作,並明確知道檔案被儲存的資料夾位置。

確定資料夾在路徑中的位置,是進行這項操作時必須注意的事情,也是打算重新編輯一個已經存在電腦系統中的檔案時,必須注意的事情。出現的小視窗,與編寫一般文字文件時的檔案存取操作方式完全相同,因此,您若已經熟悉這種操作,便能很快體會出其中的意義。改變資料夾所處位置的層次時,以點選小視窗上方帶有翻頁箭頭的圖示來達到目的。小視窗最下方一列的檔案型態,決定了小視窗中可以顯示的檔案形式。最重要的一項操作,則為選擇『確定』欄位,壓下滑鼠左鍵前!必須確定有您自己決定賦予的檔案名稱,而且型態延伸(type extension) 尾綴用字,必須人為鍵入!!!指定為『 .F 』或『 .f 』的格式型態,否則, FORTH 系統自己無法識別出,這是一個屬於系統可以自由操作的標準 FORTH 源程式檔案。

假設存好了編寫的程式檔案後,電腦系統回到了開機備便後的一般螢幕顯示狀況,等待使用者進行下一步操作。

第三個要學的操作是載入檔案:在電腦系統的觀念上,剛才編寫完成的檔案,它的地位等同於一個 FORTH 系統,只不過 FORTH 系統是一個可以被電腦直接執行的檔案,而編寫完成的檔案,是一個文字形式尚不能執行的檔案。使用者必須操作電腦,以 FORTH 系統載入文字程式檔案,剛才所編寫完成的檔案程式,才能在 FORTH 系統中執行。

載入程式的方法,可以像傳統的 FORTH 系統一樣,在螢幕畫面上點選兩次 FORTH 系統的圖示,讓 FORTH 系統開始執行之後,使用 FLOAD 或 INCLUDE 現成指令來達到目的。執行這種指令時,必須注意的事項為: FORTH 系統現行指定路徑(Path)的處境,必須與打算載入檔案之路徑的處境,處於同一資料夾(Directory)之下。只有這樣,才允許在 FLOAD 或 INCLUDE 指令之後,直接使用指定的檔案名稱,且檔案名稱型態延伸尾綴,必須是『 .F 』式的 FORTH 系統專用格式。若檔案並非處於上述資料夾內,輸入的檔案全名,則必須是包括前引路徑的全長名稱。具有此種限制性的典型指令操作方式,例如:

FLOAD filename.F 或 INCLUDE filename.F

另外一個比較有效率的程式載入方法,為直接以滑鼠操作。

就 Win32Forth 系統而言,我們強烈建議使用者,養成這種可以快速載入程式的操作方法。一方面是可以節省程式的發展時間,另一方面當程式的檔名為長串中文名稱時,依靠上述傳統的載入檔案操作方式,就顯得太不實際了。能在整個 FORTH 系統中自由使用中文,是現行 Win32Forth 系統的一項主要特色,現在還很難見到別的程式語言能夠全面融入中文, Win32Forth 系統既然可以,為什麼不讓這樣的特性盡量發揮?假設使用者已經習慣於輸入中文,如果上述剛建成的檔名給的是『範例操作程式.F』,在練習操作載入程式到達熟練的程度前,不用滑鼠來進行載入操作,就會是一件令人相當苦惱的事情。

以滑鼠操作載入檔案程式的詳細步驟如下:

以滑鼠點選兩次,啟動 Win32Forth 系統後,此系統呈現的標準視窗上緣,有一橫列英文文字分類的操作選單。

將滑鼠移到顯示 File(檔案)的位置,點選一次,立刻會在此處以小方塊顯示隸屬於 File 此類之下的所有子項菜單式操作選擇。

將滑鼠移到 Load Forth File… 位置,點選一次後,與存檔操作時類似的另一個小視窗就會顯示出來。

操作滑鼠,到打算載入系統的檔案名稱位置,點選一次,這個檔案的名稱就會自動跳入小視窗下方原為空白的『檔名』欄內。

移動滑鼠到小視窗右下角顯示『開啟』的位置,點選一次,小視窗消失,而載入效果就會立即呈現在 Win32Forth 系統的標準視窗內。

載入成功,則會有一列包括全路徑的檔案名稱先行顯示,緊接於後顯示 ok 。

載入失敗,則顯示一大堆告訴使用者為何失敗的文字說明。

依上述操作載入成功之後,就在 Win32Forth 標準視窗游標顯示的位置,直接輸入練習建立的『 hi 』指令,可以立刻看到印出『 Hello ! How is going? 』的效果,這就是 Win32Forth 。

其他的程式編輯器

前節所述程式編輯操作的整個過程,足夠應用於一般性的程式編寫了。

視窗系統提供了使用者可以開啟多重視窗的功能,因此, Win32Forth 系統也允許使用者執行此系統時,可以開啟其他的視窗,甚至於系統在執行過程發生反常例外(Exception)現象時,有時會自動以另外一個小視窗來顯示警告訊息,或自動跳出另一個視窗,顯示引致系統執行錯誤的程式檔案內容,以利使用者立即進行除錯前的檢視。

現行文書編輯系統,很強調依靠滑鼠操作,由左上角向右下角拖曳於指定文句,使指定區域反白顯示代表被選定(Selected)了,再到操作選單中點選 Edit 『編輯』類,在菜單式選項中選取 Copy (Highlighted Text)『複製』,或選取 Paste (to Keyboard)『貼上』,讓系統來處理文書編輯時常執行的文字編寫功能。這樣的操作是 Win32Forth 系統發展程式時,經常要做的操作動作。

能夠同時開啟好幾個視窗,能夠使用滑鼠操作選擇(Select)、複製(Copy)、貼上(Paste) ..... 等功能,『 WinEd 』程式編輯器的功能,顯示它就是一個一般性的文書編輯器,與流行的文書編輯系統比較,『 WinEd 』的功能接近標準的素文字編輯器『 NotePad 』,但與『 Word 』或『 WordPad 』不同,因此,由『 NotePad 』編寫而成的程式, Win32Forth 系統可以接受,另外兩個則不行。雖然如此,『 WinEd 』與『 NotePad 』對於 Win32Forth 系統而言,意義不同,因為, Win32Forth 系統可以直接對『 WinEd 』操控的檔案操作,但系統沒有直接對『 NotePad 』操控檔案操作的相關指令,由這兩個編輯系統顯示同一個檔案時,畫面有所不同,就可以明顯的區別出來。

大部份 eForth 系統的程式編輯器,由於系統性能強調短小精悍的關係,均不注重於增進編輯器性能的設計,有些用法還根本就不設計編輯器,而依附在對其作業的大型 FORTH 系統,以便控存發展中的程式。作者使用過可以接受單純以 PE II 編寫程式的 eForth 系統、或檔案幕式的編輯系統、或檔案塊式的編輯系統、或自行開啟新視窗式的檔案編輯系統 ..... 等,也曾倣效 polyForth 系統提供的資源,自行設計過 eForth 的傳統塊式程式編輯器。這些編輯器的操作方式因系統而異,不在討論範圍,讀者必須於使用各該系統時,自行適應。


2026年5月2日 星期六

自建動態連結程式庫檔案

自建動態連結程式庫檔案


曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
2 May 2026


本文係十五年前還用 Win32Forth 系統時所留下的文章,是為了使用 FORTH 系統外部資源時所需要建立之一種觀念的文章記錄。

這是一種有關 FORTH 系統中,叫用微軟作業系統內應用程式界面(API)的觀念問題。這原本不是我喜歡談論的問題,我不喜歡 API ,所以也一直不去碰觸,我個人有能力完全隔離 API ,繼續輕鬆使用任何新出現的 FORTH 系統,對許多人而言,尤其是 FORTH 的新進使用者,則可能不習慣這樣子使用 FORTH 。

API 的出現,確實因大量污染了 FORTH 系統,而影響許多人保持接觸 FORTH 的意願,影響了FORTH 社群的發展,非我所願。有障礙,我們就突破,不如直接研究 API 。

我可以舉一個很簡單的實例,將 API 建進動態連結程式庫(DLL)式的檔案,由 FORTH 系統來自由叫用,讓大家都可以輕鬆的了解,這種使用的技術是如何被引進 FORTH 系統的?此後您就不用再摒棄它,繼而善用它。為此,我才在 60 歲時重新接觸 C 的環境,也只有這樣,才能解決這項別人不願意解決的問題,解決問題的過程,也算得上是一項寶貴的經驗,留下記錄,提供大家參考。

我有能力將實際範例寫得非常簡單易懂,只需四列簡直不像程式的 C 語言程式,而且其中還有兩列只是單個大括弧,就能辦到這樣的事情,但您得仔細的看完本文。這樣的教材,期望能夠告訴大家,精簡的傳遞我們專精的 FORTH 技術是很重要的,如果您是 C 式程式語言專家,也請不要輕視像這樣的 C 程式,它足以解釋許多人搞不清楚的問題,我相信任何人只要肯做,絕對能做得比我還好,我指的是要比四列還少,長篇 C 程式會讓 FORTH 的使用者看不懂,我實在是忍不住了才這樣做的,中國人避免用複雜問題欺侮中國人,四列就夠了。

開始研究問題時,上網瀏覽相關資料是免不了的事情,但很難找出一篇純粹以 FORTH 立場來實現這項技術的精簡文章,也就是說,還沒有這方面正式的相關 FORTH 教材,可以直接翻譯來供給大家參考使用。

我在完成解說問題前,習慣於自己先行實現論述的內容,網路上可以找出無數不在乎 FORTH 而偏重於 C 方面的資料,講述如何在 C 環境中建 DLL ,例如:您就由 Google 鍵入『building and using DLLs』,有數不清的文章可以免費閱讀,我看了上百篇,這些東西不像 FORTH ,是完全不透通的資料,您不能過問或自己追查資料內所敘述的操作步驟,生產 C 編譯器的公司發行產品時,資料內寫了使用者該這樣做,您就只好照著做,否則得不到該系統告訴您的結果,可是得到的結果卻不能用。我使用 FORTH 幾十年,從來沒受過這種氣,非常委屈,為了完成本文,我忍下來了,也希望非得用 C 來解決問題不可的電腦程式用法,這是最後一次。

網上資料通常使用了很大且空洞的篇幅,與花俏又多色的畫面來美化成品,當我記不住一大堆指定操作命令或參數用名時,想將關鍵對象摘錄下來,也出現了許多麻煩,要浪費時間去掉那些沒價值的東西,然後才能以比較節省成本的方式,將重要資料印出來仔細研究,避免浪費紙張、墨水,凡事我都講求節省,我只重內容而不重外表,因此,不會浪費紙張來印出視窗畫面。

想寫本文之前,心中早已具有在 FORTH 環境中如何使用 DLL 內之 API 的觀念,只是不曾依樣自建一個來試一試。這一次這樣做,當然不可能一次就能成功,而且每次在純 C 環境中試出一個成品後,滿懷自信的相信『 C 號稱其轉置能力比別種程式語言強』之說法,立刻就將結果轉進 FORTH 試用,但送進 FORTH 就是不能實現,結果不但不成,問題一耗就是三天不能前進。起先,我以為自己多年不接觸新進電腦系統,沒搞清楚一些奇怪的電腦術語,誤解了說明資料的意思,特地在參加本地 WLUG 組織 Linux 例行活動時,請教這裡的專家,是不是我誤解了術語?做了錯誤的『死性』操作。結果不但是無功而返,反而是我得大費口舌,必須對 Linux 專家講解 C 中的編譯程序概念,當然這些概念也不是我發明的,全是由其他相關資料讀來的,卻對我想解決的問題毫無幫助, Linux 專家大部份也對這些『死程序』一知半解,規定性的說明資料實在太多了,只講英文的電腦專家也看不懂那些英文,但那些英文沒有字典上查不到的單字。

我人在紐西蘭,能直接來往的環境中,現在已經沒有會講中文,還具備電腦技術與共同興趣的朋友,網路上呼叫台灣的朋友,實際上也沒那麼方便,鑽牛角尖的問題只好靠自己了。但是,我還是相當幸運,我有一個在大學裡主修過電腦科學的女婿,而且他仍在電腦科技的領域工作,只要我將電腦問題解釋清楚,通常他都可以協助我解決關鍵問題。女婿平日上班時要忙他自己的工作,假日裡需要累過幾天工作後的休息,我只好自己先行釐清問題,過濾出非問不可的項目,與女婿約好時間逐項解決,能完成這本文,寫下這些記錄,不得不感謝他的協助,這裡只有他能完全聽懂我向他解釋的 FORTH 觀念,他不用 FORTH ,但能幫助我解決 FORTH 以外的電腦問題。

從 FORTH 環境叫用 DLL 中的 API 技術,不是近幾年的最新技術,自從 DLL 使用資料公告給大眾,以便大家可以用它來發展軟體產品時, FORTH 也就同時建立了這項使用方法,但在 90 年代以前,沒有 DLL 的時代, FORTH 系統中不建這種功能,我們卻早已用過這樣的呼叫,只不過以前稱為作業系統 BIOS 中公用中斷程式 INT xx 的功能呼叫而已。

既然這是一項老技術,大家又不熟悉,要搞清它,最好就從一個 FORTH 系統的發展歷史資料中探討相關的問題,如此便能了解當初系統設計這項功能的來龍去脈。我們需要透過一個目前共同推廣的系統才能進行溝通,以 Win32Forth 系統為主,所以大家最好也應了解這個系統存在的可貴。我在網文中找到系統原始設計者支湯姆(Tom Zimmer)回答大眾的信件,才明確的知道某些動態連結程式送進 FORTH 系統時,跑不通的關鍵問題所在。我有種感覺,無論您學那一門學問,這門學問的發展歷史也該仔細讀一讀,否則就很難了解這門學問的精要,此即一例。另外,我從網文中也讀到了,當年歐洲 FORTH 組織,曾因 Tom Zimmer 公開 Win32Forth 系統,供大家免費使用,而頒給他一份金質獎章,這是我在 FORTH 界從未見過的殊榮,也令我更加尊重 Tom Zimmer 及其 Win32Forth 系統。

除此之外,我根據歷史記錄,仔細回顧所有業經公開之 Win32Forth 的所有版本,才知道只有 Fig 總會網站上提供的最原始 V4.2 版是原作者的創作,此後的所有其他版本,均經其他組織刻意修改過,而主要的修改內容,竟然就是以配上後來新出產的 .DLL 檔案內不同之 API 應用,為主要區別所在。讀者可以回顧 V4.2 原始版本的內容,眾多檔案中只有一個容量 382K 名為 wincon.dll 的檔案為這個系統所用 API 的主要來源,沒有其他的 .DLL 檔案了,後來的版本都加裝了其他的 .DLL 檔案,以便取得其他功能的 API 。

因此,V4.2 版以後的 Win32Forth 系統都很花俏,注重畫面表現,雖也修正了幾項後來發現的錯誤,實質內容並沒有很大的改變,但卻將幾項傳統上很有用的 FORTH 專屬性能,刻意的埋沒掉了,例如:將很有用的蛻變編譯(meta compile)操作,轉換成只選畫面中一個指定字母的『死性』操作,這樣做就讓後來接觸 FORTH 的人,更不容易了解蛻變編譯的概念。發行 V4.2 以後版本的幾個組織,好像有意扼殺 FORTH 的研究發展,希望大家只用他們做給您的系統就算了,這些新版本的性能除了畫面漂亮外沒有特出處。

只有一個組織,比較老實,網頁畫面記錄,直到 2009 年,依然只推行 V4.2 的版本,我完全能體會各個組織的用心,但我不願逐一介紹,或區分這些網站。 Tom Zimmer 個人已經不再在 Win32Forth 系統上下功夫,以後的路子要靠使用者自己走,如何走?也由使用者自己決定。也許了解這些歷史,才有一面鏡子,能夠照亮過去、現在與未來。

當初,我只選擇 Fig 總會提供的標準 V4.2 版本發展 ABC FORTH 是正確的,但我也不排斥別人的成就,我曾誇口,任何具有浮點計算功能的 FORTH 系統,我都有能力為它加裝我所設計、可用 50 年以上的 ABC FORTH 系統。目前號稱最新推出的 Win32Forth 版本是 V6.15 版,我當然也能加裝,為了實現我的信念,我就重新在這個版本上實現了同樣的設計,做完了這件事情,我就知道這個版本的問題在那裡了,所以敢進行上述批評。

不僅如此,我早已在數個 FORTH 系統中完成了 ABC FORTH 系統,但沒有必要繼續釋出,理由有三:
一是我不希望已經公開之 ABC FORTH 系統內的指令,被隨便更改,造成因為指令不標準,而無法使用那份『使用說明』的問題。
二是屢次重新編譯我的原始設計,是一件很容易辦到的事情,這樣做當然也容易造成它的不穩定,因此,我刻意將其固定下來後才公開推出。
三是我堅持暫時只在指定地區使用,一切相關發展便易於掌握,目前按照我的安排與大家的認真配合,應該可以達到這樣的要求。
ABC FORTH 系統架建在 Win32Forth 系統上,這個系統的所有問題,應該也都屬於我該關心的問題, API 是個問題,我就理所當然、義不容辭的應該協助解決,現在讓我們來仔細探討, API 是如何建在 DLL 中供 Win32Forth 使用的?

從網文上可以找到的相關資料,都是站在 C 的立場解釋 DLL 的,那是擅長 C 式程式語言者的做法。我要一反常態,從 FORTH 的立場來解釋這種應用,而且將純粹屬於在 C 環境內,如何叫用 DLL 中 API ?的問題排除掉,完全不予討論,那是只會搞 C 的人,才需要關心的問題,與我們無關。

在 Win32Forth 中,想要叫用一個 .DLL 檔案中的 API 來用時,可以簡單到只需使用兩個簡單現成指令 WINLIBRARY 及 CALL 。

假設,有一個在微軟作業系統中堪用的動態連結性程式,檔案的名稱為 mydll.dll ,而這個檔案內有許多具有指定名稱的『功能性程式』(請注意,並接受這個我所使用的適當專有名詞,如果您將這個術語套到文章中所有使用了 API 的位置,那麼,文章唸起來就通順了,而且,意義也淺顯了),在 C 中的術語稱為函數(function),它們實際上就是大家所謂之可以被外部程式叫來使用的 API 了, Win32Forth 也可以隨意叫來使用。我們再度假設,有一個典型的 API ,它的名稱就叫作 subb ,它的功能是將兩個整數相減後得到差值。就憑這兩個假設,就足以讓我們解釋清楚本文打算論述的主題了。此處『mydll』及『subb』是隨意給的命名。

mydll.dll 檔案一旦被放置在與 Win32Forth 系統同一個資料夾中, Win32Forth 系統執行起來之後,想直接使用這個 API 的操作,就只需要執行下列輸入指令,指令當然也能被設計進程式:

WINLIBRARY mydll.dll
10 3 CALL subb

其中,第一列是用來宣告將 mydll.dll 檔案的所有功能,連接進入 Win32Forth 系統所需使用的宣告式操作指令。而 subb 是檔案中的一個功能程式,亦即 API ,它此後就可以自由的被 Win32Forth 系統,以單一個 CALL 指令,如同上述的方式直接叫用了。這樣的教材只能告訴大家, Win32Forth 中要如何叫用一個API ,對已經存在此 FORTH 系統中,與微軟作業系統相關的原始 API ,確實只用 CALL 指令叫用就夠了,但它只是簡例,全面正式的使用,則另有當其他狀況出現時,必須注意的使用規則。例子舉得太簡單了,用法就不容易記住,但如果我教您如何親自來自製出這麼樣的一個 API 後,您就可以終生記得它的用法了。 接下來,我們不得不碰 C 了,但可以不必學得那麼多,有概念將其實現才是最重要的關鍵。

我剛開始嘗試設計這個教材時,規規矩矩的建立了好幾個標準的 C 程式語言的測試程式,吃過不少苦頭,唸了許多很基本與 C 相關的書籍,還做 60 歲自修老學生的筆記,記下 C 標準程式中有那些東西,例如:前處理指引、標準庫存程式、簡化程式中變數名稱之宣告方式、程式主體、程式敘述集合、庫存程式指令……等等等,一大堆術語,然後糊里糊塗的在 Linux 系統跑通了幾十個程式,到現在還是糊里糊塗的使用C系統,只會用系統來產生 .DLL 的檔案,但不會純用 C 自己設計一個能產生 .DLL 檔案的編譯器(Compiler)。

請不要笑我的學習經驗記錄怎麼這麼幼稚? BORLAND 公司才能設計出 TURBO C 之 compiler 的,我是何德何能啊?才玩幾天 C ,就想自己設計 compiler!直到現在,大約它外部 90% 的系統操作命令我都還不會用呢。可是,憑良心講,如果我已經在 FORTH 系統中下了同樣時間的功夫,我應該可以設計出迎合這種需要的一個簡單編譯器了,所以我是不是太小看 C 的 compiler 了?我不堅持我的答案應該是『是』或『否』,但一切事在人為。單純的用 FORTH 寫出能編譯出 .DLL 檔案的一個編譯器,不是大話也不是笑話, FORTH Inc. 發行的 swift FORTH 早就已經能夠辦到了,這個系統可以用 FORTH 直接設計出 .DLL 的檔案,十幾年前就見過它的廣告詞,幾萬元台幣才能買一套,所以大家都不知道,我也沒用過。學會能產生 .DLL 檔案的編譯器,對我而言並不困難,因為我熟悉 FORTH ,能讀通 swift FORTH 的程式,所以不是笑話。我雖對 C 只是一知半解,但已經敢寫這篇文章,教大家 FORTH 需要的部份了,這個教材裡面,一點都不需要涉及將指標指來指去,在 C 中耍帥的技術。

剛開始寫成的幾個程式,都還有 C 的規矩,等到我搞通問題,將結果送進 FORTH 系統,實際執行成功後,我就花時間搞調皮處理,目的就是想寫出本文,教大家一個簡單到不能再簡單之程度的範例,讓大家終生記得。

我的程式只有下列四列:

int subb(int x, int y)
{
return y – x ;
}

就這樣,沒了。這就是我全部的源程式教材,是不是很可笑?可是,它通過了編譯,編譯過程中沒有出現任何的錯誤訊息(Error message),程式被製成 14.7K 份量的 .dll 式檔案,送進了 Win32Forth 系統,實現了前述兩列直接執行的 WINLIBRARY 及 CALL 指令要求,我得到了正確答案 7。

從上列程式看起來,好像我很不會寫程式,不適合當程式設計師。我承認我的程式很不漂亮,很不規矩,很不標準,很不像樣,簡直就在亂搞,不像是程式。可是,都執行出了我自己想要的結果。這些程式跟高手的作品比較起來,我確實只能算是一個調皮的程式實踐者。這些東西我都沒有打算要賣錢,搞得那麼漂亮幹什麼?為了解決問題我才設計程式,不是嗎?只要能解決問題,我為什麼一定要守那些不必要的規矩?所以四列就是四列,不必多列。

接下來,我們要探討上述教材,能夠被執行出實質結果的一些關鍵問題,這一部份全都是重要觀念,文章可不是信手拈來隨便寫成的,得到確切的結論前,當然也必須實現遠多於需要的研究與測試。

四列 C 例程式, API 的名稱為 subb ,按 C 規矩來解釋:

執行 subb 前,系統堆疊上要先準備好兩個整數,一個是 int x ,另一個是 int y ,它們被包括在小括弧中,注意 x 與 y 被宣告的次序, x 在先, y 在後。 subb 功能程式的執行內容,就是被包括在兩個大括弧之間的程式敘述,此例被刻意簡化成只做一件事,就是將 y-x 計算後的整數結果回傳給系統,而且就將其放置在堆疊上。由於整個功能程式 subb 執行後的結果,回傳給系統的資料型態(Data type)為一個整數,因此,在 subb 的名稱前面,需要使用 int 前引。沒了,這就是全部 C 程式的詳細解釋。

我在 ABC FORTH 系統中,設計了屬於 BASIC 式程式的大括弧指令,也做了跟這裡大括弧之意義完全一樣的功能設計,但當初設計 ABC FORTH 前,從未探討過 C 中大括弧的真正意義。從這裡看起來, C 中大括弧的意義,跟我為 BASIC 設計的大括弧意義,完全一樣。換句話說,我個人就有能力設計出一些 C 的編譯程式,但我可以把編譯環境設計得比 C 更人性、更友善。

回到 Win32Forth 的環境,我們實現這份教材時使用了『10 3 CALL subb』一列參數與指令,系統執行時, 10 比 3 先進堆疊,傳到了 C 環境後, 3 卻先傳給x , 10 才隨後傳給 y ,但兩個數字都沒有消耗掉,仍然保留在 FORTH 系統的堆疊上,執行完 y-x 後得到了 7 ,這個數字再度被擠進 FORTH 的堆疊,才結束程式。所以, FORTH 系統執行完這一列程式後,堆疊上有 3 個數字: 10、3、7 ,這就是全部外顯的執行結果。

我設計的教材,指令執行前,需要兩個整數,而且故意用減法,兩種系統,在堆疊參數的傳遞上,誰先?誰後?的不同效果,才能顯現出來。程式雖簡單,卻代表了一切,兩個大括弧間的程式敘述內容想寫進多少?那是專搞 C 的人才需要傷腦筋的事情,與 FORTH 無關,所以您不必學,我也不必教。想設計出許多個不同的功能程式,全部放在同一個 mydll.dll 程式中備用,依上例增建與 subb 功能程式同樣的結構,就能辦到,這也不是我該教的問題。您有沒有想過?用這種方法可以全用 C 程式提供 FORTH 系統檔案界面功能、浮點計算功能、繪圖界面功能……等等,這也不是我想教的課題。全力以赴,都能辦到,但界面並不單純,例如:浮點數算完了還是得顯示出來,就又非用 FORTH 不可了,所以沒有人用 C 這樣子為 FORTH 系統設計程式。

至於 FORTH 與 C 兩個系統間的參數傳遞格式,我們只需就 FORTH 這方面提示一下就夠了。浮點數也能傳遞,但您要先搞清楚格式,兩者必須相同。如果傳遞的是記憶體位址,那麼,在 C 環境中使用的是絕對位址,在 Win32Forth 系統中使用的是相對位址,二者可執行 rel>abs 或 abs>rel 指令來換算。如果傳遞的是字串時,在 Win32Forth 系統中使用特殊的『z”………”』指令,來進行包裹。由於這種技術並非標準用法,而且我也不認為再過多少年後,這種使用技術還能永遠存在,所以這些說明,只宜作為應景教材。

※以下係關於 C 系統方面的操作細節,與 FORTH 無關。

使用 C 的系統來產生我們所需要的 .DLL 檔案,方法不是固定的只有一種,因為就 C 的立場而言,全世界所有供應 C 系統的公司,也未將此事統一起來。曾經較為流行的 C++ 或 C# ,所安排出來的作法,是在源程式緊接著前處理指引宣告之後,增加一列額外的宣告:

extern “C” {………………}

然後在功能程式(Function)的前引,再增加另一個宣告指令,例如:

_declspec(dllexpot) FUNCTION-NAME() {………………}

如此設計程式, C 系統才能編譯出您想要的 .DLL 程式來。也許其他的 C 系統還有其他的設計格式,但都只能從研讀發行 C 系統公司所提供的使用說明書中,才能得知詳細的使用方法,沒用到前沒有先行學習的價值。

這樣的用法,有一個很大的缺點,想造 .DLL 程式的人,必須取得 C 的源程式,然後明確的按照要求,如同上述的方式,在源程式的相關位置,填入上述宣告或指令。必須有源程式,還必須修改,系統程式可能還得花錢買,隨後還得進行特殊的編譯操作,才能得到可用的結果,以這種方式實現自行建造 .DLL 的整套方法,確實不太好。因此,我僅只保留收集所得的相關資料,而不進入那種環境去研究發展。

評估現況之後,覺得使用公益軟體,來了解這項 FORTH 與 C 間的初步性能,可能比較理想,於是先從 Linux 中的 gcc 開始發展, gcc 建 .DLL 的方式有一個很方便的特點,就是完全不用修改基本的 C 源程式內容,而只需在操作 gcc 的編譯過程中,使用指定操作命令按步驟操作,使用者只須集中注意力於編譯操作上。

C 的範例源程式並不難建立,但有經驗的使用者,建議使用只產生素文字的編輯器,設計 C 的源程式,在微軟作業系統中指的是記事本(NotePad)而非文件檔案編輯器(WordPad),以避免文字控制碼夾雜在源程式中,妨礙後續的編譯操作。

此前,我曾使用過此 Linux 系統中的 gcc ,編譯出 gForth 及丁陳老師傳給我的 eForth in C 源程式,所以系統值得信賴。根據收集到的資料顯示,以 gcc 產生 .DLL 檔案,只有兩次的操作,例如:源程式的檔案名稱假設為 mydll.c ,則先用此源程式編譯出一個物件檔案(Object file),操作方式為:

gcc –c mydll.c

操作完成後,可以 ls 操作命令檢查資料夾內的檔案,就會增加了一個名為 mydll.o 的物件檔。接下來,要用這個物件檔,直接產生一個外界可以使用的動態連結檔,也就是我們想要的 .DLL 檔案。操作時要用到一個意義上為外界可以共享(Shared)的操作命令,操作方式為:

gcc –shared -o mydll.dll mydll.o
如此便完成了我們想要的 mydll.dll 共享式庫存程式檔案,這兩個操作程序,算是已經被我濃縮到了最簡單的程度。

隨後,我利用 E-mail 傳送信件時夾帶檔案的方式,發了一封自己傳給自己的信件,再到另外一台電腦上取出此 mydll.dll 檔案,放置在 Win32Forth V4.2 系統的資料夾內,立刻以上述指令直接操作測試,不幸失敗了,失敗現象是一執行到叫用的功能程式時,整個視窗就立刻消失了,因不留痕跡,無法除錯。

檢討問題時,直覺想到也許就是因為作業系統完全不同,所以不能這樣用。於是在網路上下載了一個 cygwin 的系統,它是一個在微軟作業系統中模擬 unix 作業系統的設計,提供相同功能的 gcc ,我在這個系統中重新做了許多次與 Linux 中完全相同的操作,並將結果直接送進 Win32Forth V6.14 測試,仍然失敗了,失敗現象比前一狀況好了一些,視窗沒有消失,畫面上顯示錯誤訊息,告知找不到一個稱為 cygwin1.dll 的相關庫存程式,於是,我從下載的系統 bin 資料夾內複製了這個程式,併同 mydll.dll 一起放進 Win32Forth V6.14系 統的資料夾內,再度進行同樣的操作,仍然失敗,視窗畫面也消失了。

最後,我仔細思考問題所在,並從網路上印出了一篇 Tom Zimmer 於 1997-12-04 寫給 Thomas 的網路論壇公開信,信中強調, Win32Forth 當初設計時,考慮到微軟作業系統提供的動態連結程式庫,佔用了記憶體的固定位址,任何程式不得侵犯,否則就不能在微軟的作業系統中執行,因此, Win32Forth 系統中的程式碼改採以相對位址的方式被執行,速度會慢一點,但此系統便可以被彈性的安排在記憶體的任何位置來執行。信中也提到,凡想被 Win32Forth 系統叫用的 .DLL 程式,被叫用而進入系統時,絕對不可以使用與微軟作業系統衝突的記憶體位置。關鍵性的三句話是:

1. WindowsNT allows an absolute program in the area between 0x400000 and about 0x600000.
windows95 I believe places programs well below this.
So if you are specifying an absolute program address below 0x400000, then it won’t run under WindowsNT.
2. You have to be careful to not allocate the same absolute address to two Dlls.
3. This is basically why I decided to make Win32Forth use relative addressing. It is slower, but more flexible.

因此,我直覺上知道前述的兩個 gcc 都產製了與微軟作業系統衝突的 .DLL 檔案。要解決這個問題,只能找合乎規格的 C 編譯系統來用,因為只是試用,不值得花錢買任何系統,但公益系統必須是藉由微軟的基礎開發出來的系統,才會考慮產生的 .DLL 檔案程式,執行時不與微軟作業系統衝突的問題,我缺乏這方面的概念,只好找女婿幫忙,他很快的從網路上找到了我需要的東西,就是 MinGW 系統,裡面也有 gcc ,但要到 DOS 的原始視窗中去執行,執行的結果可以與 gcc 暫時同放置在 MinGW 的 bin 資料夾內,要使用時,才將 mydll.dll 檔案移到希望放置的資料夾中去。

做好了這樣的安排,一次便完成了希望的測試,得到了很正確的結果,證明了根據 Tom Zimmer 信中所言而思考的方向是對的。有了結果,我便有機會進行精簡範例程式的發展,最後,將寫成這篇教材所需要的範例程式,精簡成了只剩四列。

妥善操作 gcc 所需用到的整體操作命令選項(Overall options),是能夠完成自行產製 .DLL 檔案的最重要關鍵,有關整體操作命令選項的使用資料非常多,而且不容易建立概念,想熟悉它們,最好的方式,還是從網路上參考別人完成的實例,並練習實作後,體會操作結果所表示的意義,才能逐漸的熟悉起來,做這件事,必須在學習 C 程式設計方面下許多功夫,操作這些東西,完全沒有學問,所以我也很不想學。

講完了這些技術,並不表示一切就到此為止,您如果好學,在 FORTH 的領域,完全可以學通這種兩個程式語言間的橋接技術, FORTH 系統是完全透通的,使用 SEE 指令,可以直接追蹤這些指令是如何設計而成的,看懂了程式與系統的意義,您也可以在自己的環境中依樣設計一個,除了 FORTH ,這個世界目前還沒有任何其他程式語言,能夠提供如此健全的功能給使用者。

如果大家都從小就開始學 FORTH 程式語言,會有許多好處,它不僅只是可以供您寫程式解問題而已,它還提供了完全可以學得通的一切與電腦有關之哲理。如果您也像我一樣使用了 FORTH 已經 30 幾年,能夠像我一樣將 FORTH 系統執行的原理寫成文章,那麼,上述文章中大家可能會認為是我在講笑話的事情,就不再會被認為是笑話了。

我可以不去追蹤本文所論述的這項技術所牽扯到的指令,單憑自己的想法,設計出另一套由 FORTH 去叫用其他程式語言功能程式的指令來,問題只在於需不需要一直發展這樣的程式?我總認為別的程式語言最後都會死去,而永遠都可以存在的 FORTH ,只能在別的程式語言還活著的時候,跟它們連一連,等它們都死掉了,連在一起也就沒有任何用處了,到頭來還是只剩下 FORTH 。這幾十年來的日子, FORTH 是怎麼過的?好像是千山它都獨行,請不必相送。

早期的系統駐留功能(Resident 例如:side kick用法),我用幾個指令就能完成設計,只要不搞破壞,讓兩個系統可以串來串去,不也是互相叫用功能程式嗎?我們曾用這樣的方式請 BASIC 幫忙算浮點計算,也讓 FORTH 幫忙 BASIC 執行一些程式,將結果放進 BASIC 固定的參數堆放位置。

我親自設計過好幾次多工程式(Multi tasking),用得漂亮時,只控管一個使用者指標 UP(User Pointer)的技巧,就能讓原 FORTH 系統完全停頓下來,轉執行其他任意的別種功能程式,別的功能程式結束了,仍然只依靠那個被控管的 UP 指標值,再執行很少的低階快速指令,就能恢復整個 FORTH 系統的繼續執行。 使用同樣的技巧,當然也能讓 FORTH 去執行 .DLL 內的功能程式。本文所探討的 FORTH 指令,大部份都在執行功能程式名稱的處理,接受 CALL 的後續輸入字串後,到 .DLL 的檔案內去尋找這個功能程式名稱所在的位置,找到了就取得這個功能程式該有的起始執行位址,合理控管 UP ,就能在執行完功能程式後,恢復 FORTH 系統的原有正常狀況。我也因此能夠體會出為什麼 Win32Forth 系統在執行完 CALL 功能程式之後,從堆疊傳遞過去的參數,依然還擺在原來的堆疊位置,交還給 FORTH 系統。我講得出這樣的執行機制,我為什麼不能自己也設計出這樣的程式來?

進一步誇口,我已經很熟練於操作我自己設計的檔案讀寫程式了,我能看到所有其他檔案程式的內容,我讀得進別人的標準結構,我當然也設計得出別人所需要的結構,因為工作只是填資料。那麼,前述所說的笑話就不是笑話,單用 FORTH 設計一個可以產生.DLL 程式供別人使用的檔案,當然能夠辦到,這就是產生 .DLL 程式編譯器的設計方法,我不是在講笑話吧?我用 C 辦不到,我用 FORTH 絕對辦得到,現今,別人已經辦到了,想做時,也能拿來參考。

而且,早就有商售 FORTH 系統,在網頁上做廣告,強調可以直接叫用 Visual BASIC 編譯產生的程式,我卻不認為這是甚麼偉大的新技術,也不值得投入太多精力去發展,因為, Visual BASIC 也早就開始逐漸地沒落了。叫用其它程式語言編譯出來的程式之技術,最後的處境都將相同,等到發行公司因無利可圖而不再賣這個系統時,前面的努力,都會成為泡影。為了節省非常有限的 FORTH 人力,這種發展就不必重覆浪費了。

目前, C 式語言還在流行階段,熟悉這些同類程式語言的讀者,可以參考本章的內容,依樣畫葫蘆,滿足一下新奇感就夠了,我並不鼓勵長期這樣子發展系統,因為產生的結果,就像 C 式程式語言的本質一樣,系統本身是完全不透通的,對技術的傳承,完全沒有幫助。我也希望,此後永遠不再需要使用 C 來寫程式。