2026年2月2日 星期一

一畝三分地的程式人生

一畝三分地的程式人生


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


地不大,就只有 866.67 平方公尺。

我現在住家的總地積為 1012 平方公尺,是座落於市區的大平房,後院裡有個大隱於市的小菜圃(12公尺 X 2公尺),不在菜圃範圍內的土地,也都可以隨便種植東西。

我只使用 Forth 程式語言設計程式,桌機、手提、單板電腦我都有,都裝 Forth ,研究問題需要寫程式時,盡量只用 Forth 解決問題。

這樣的人生格局,已經步入坐 7 望 8 的年紀,卻照樣喜歡過著一畝三分地的生活,居家種菜是這樣,發展程式技術也是這樣,好好的過日子,總是能悟出一些事物的道理。

紐西蘭的氣候四季分明,冬日裡菜圃沒有什麼東西值得種植。一方面是低於 10 度 C 的溫度時,植物停止生長。另方面是寒冷的冬天很不適合從事戶外農務工作,我們就更應該順應天理,讓土地休養生息,春天到了才再重新開始。我從小就喜歡種植蔬菜瓜果,有 70 年以上的經驗。我住過的房子都帶有院落,可以經營一畝三分地式的精緻農作,這是一種健康的人生,不只是所有的產物都不用化學肥料、不用化學農藥,除草、墾地也只用簡單農具,我從實際工作中悟出道理,讓工作順遂、收成理想。

好的收成來自於勤奮的耕耘,農務工作也事事都得講道理。我從老鄰居 Norman 先生那裡學到,紐西蘭要等到十月份第四個禮拜一的勞動節以後才適合開始播種、種菜,除非種子難以買到,否則菜種最好不要自己培育,自己繁衍的種子容易劣化,價格又很便宜,市價大約都是每小包2.5元紐幣,份量適當。我今年就買了玉米、小黃瓜的種子。

現在,小黃瓜的收成都已到了尾聲,種植它時有些特性。需要育苗後移植,才不會被無殼蝸牛吃掉。種下去之後就不宜再做除草之事。它是淺根性植物,除草、翻土都會導致瓜株枯萎,沒有收成。烈日下只宜澆水,要吃嫩瓜就得勤收成,新瓜才能後繼跟上,天天去收成就天天有得吃。小黃瓜是不宜授粉的瓜,因為授粉的瓜會苦,公花甚至於可以全摘除了加進蛋花湯食用,我覺得很好吃。

甜玉米是去年 11 月 3 日播的種,生長期 100 天,今年 2 月 10 開始適合收成,就是這幾天。我早已練就了識別玉米成熟與否的方法,用手輕捏玉米的尾端,顆粒都已飽滿圓滾了,就是成熟度夠了。這時若不收成,鳥都會飛來啄食,螞蟻也上來產卵、繁殖,這樣的損失就很划不來了。

菜圃裡我還大量種植了青蔥與空心菜,這兩種東西都很容易種,居家生活就能天天都可就地採收食用,方便、乾淨是我家這個大隱於市菜圃的最大好處。

我割草後都倒進堆肥槽,槽就建在菜圃的正中央,槽的兩邊特別適合種植瓠瓜,等同於此瓜不用施肥,只須大量澆水,堆肥分解得快,瓠瓜大量吸收。只種一棵,就能每天授粉、每天收成。我們吃起來很奢侈,每天都只吃一個低於一公斤重量的瓠瓜,種子都還沒有長成就收來吃,嫩而略帶甜味,一棵每年都能有超過一百個授粉後才能獲得的嫩瓜。

小黃瓜邊不宜除草,空心菜與青蔥邊就得勤於除草才有收成。親身做過除草工作才能悟出除草的道理,草芽很小時,想除也難除,草苗長大了就吃肥、浪費養分,標的菜株必定長不好。我除草時自行判斷,要等雜草長得夠高了才動手。草苗若長太高、根部已經分枝後就難除了。拔草時只宜一根一根的拔,手抓最底部,直接連根拔起,就能除得乾乾淨淨。貪心多抓幾根,根部反而容易斷在土裡,這樣拔,只除了表面,沒有春風,只要有烈日也照樣重新又生而且大長。除草並非只做一次便可,何時又該除草了?只能自己觀察。除草的重點學問是你得分得出那些東西才是雜草,否則會在除草時拔了青蔥。我的菜圃內早已是苜蓿大量氾濫,它卻是有益植物,它不容易連根拔除,最好也別拔除,因為它的球根有將空氣中的氮固定於根部轉換成為氮肥的效果,留在原地,地裡就肥。我種甜玉米的區域,長滿了苜蓿,它除了具有保溼、供肥的效果,苜蓿還排擠其他雜草,有多重好處。

一畝三分地的人生哲理是這種生活方式並不足以可以孤立、隔離、健康的維生,我們每星期六仍須去跳蚤市場買中國青菜。小白菜、白蘿蔔、大白菜、甜的高麗菜等是我們的最愛,這幾種菜也只有華人的菜地供應得了,一般超市不是不賣就是品種欠佳。我們只在超市買菠菜、番茄、茼蒿、芹菜等,因為超市這方面的供貨比較乾淨,易於進行食用前的處裡。

這些長期培養出來的規矩,就像寫程式一樣,是已經駕輕就熟的生活程式。看看這些今晨拍攝的菜圃照片,我以實際成果展示這種一畝三分地的踏實人生。


我在公開貼出的文章中很少展示繪圖方面的程式作品,一方面是這種程式的規模都比較大,另方面是能夠純用 Forth 繪圖的系統並不太多,所以我就少談。

今天,我以經營一畝三分地過活的精神,簡介一個純由我自己發展出來的繪圖範例,它是在 Win32Forth 系統中繪製出橢圓的實例。這是還在使用 32 位元環境之作業系統時,於 Win32Forth 發展末期的零星作品,那時的程式心態就是缺什麼就建什麼,自己經營。

用過 Win32Forth 系統的人都熟知,現成可用的繪圖指令中,缺乏可以直接繪出橢圓的指令,因此,我就自建一套繪製方法。這樣的耕耘,要身體力行於實際設計,並看到輸出結果後,才能領悟出這樣的繪製程序是否好用? 它是 2015 年的產品,由於數學演算部份全用 BASIC 語法的格式書寫,多年之後,我仍能直接讀出繪製方法的重點,一目了然。

關於 Win32Forth 系統的繪圖功能,我不擬詳細解說,只能大致描述如下:
基本上,它藉著小畫家軟體的繪圖觀念建立起基礎架構,繪圖時採用物件導向程式(Object-Oriented Programming (OOP) )的書寫方法設計程式,把被繪出的圖形當作一個物件(Object)來處理。
所以,使用 :OBJECT ..... ;OBJECT 的架構框住整個繪圖程式。
程式的起頭,通常使用幾個物件導向式指令,宣告規劃出視窗物件的定點與大小、給予視窗框架的抬頭名稱或標示,然後才開始繪圖。這些物件導向式指令都以 :M ..... ;M 的格式框住其內容,最後一個 ON_PAINT: 物件導向式指令的執行內容才是整個繪圖程式的重點。
為了讓我設計之 ABC FORTH 語法的程式能與物件導向式繪圖程式相容於此系統,必須在 ON_PAINT: 指令結束的末尾,採用一次 [ CLASSES ] 立即執行式的宣告,作為結束,繪完圖後才能安返系統。

我的橢圓繪圖方式係採用表示橢圓的參數方程式(Parametric Equation)進行繪圖。橢圓的關鍵參數是其長軸與短軸,也就是 a 與 b 參數的大小,繪出時需要知道橢圓的中心座標點(可以在視窗中任意指定)與被旋轉的角度 d (逆時鐘方向旋轉),這樣就能在平面上任意一點繪出各種橢圓。尺寸過大也沒有關係,觀念上就是畫到外面去了,不影響橢圓圖形的形成。有關橢圓之數學表示式及其幾何圖形的意義,是高中生學習二次方程時的課題,本文完全省略不討論,請自行複習相關資料。至於為什麼採用計算出橢圓參數方程式的方式來繪圖?係取其方便而用之。
簡單的繪圖觀念只須依靠提筆(MOVETO:)與劃線(LINETO:)兩個物件導向式指令就能完成。為了繪出粗紅線條,我另用 BRUSHCOLOR: 與 FILLCIRCLE: 兩個物件導向式指令展示了成果。
這套繪圖程式有那些現成的物件導向指令功能? 請自行參考系統,內容龐大,但是沒有能直接繪出橢圓的指令。
範例程式我只貼出一份。今天早上我親自在 Ubuntu 20.04 作業系統加裝了 wine 軟體的環境中,啟動了 ABC660ForthV61505.exe 執行出此範例程式後,才操作 Shift-PrintScreen 硬拷貝視窗物件,最後得到這張完成圖。
另外加貼一些於 2015 年採用各種同類型程式繪製出來的圖樣,供作參考。由於程式都不簡短,因此只刊圖形,不刊程式。

一畝三分地的程式人生就是這樣形成的,我自覺這些題材有點樂趣,創作也值得與大家分享。


 
\ Parametric Equation plot
\ Copyright (C)2015 Ching-Tang Tseng, All rights reserved

3 INTEGERS I XI YI
10 REALS S/D XYmax t dt lt ht x y x0 y0
4 reals a b d r

\ (1)Points setting
400 VALUE N

N ARRAY XX
N ARRAY YY

\ (2)Upper and Lower t limitation setting
: init
{{ lt = 0 }} {{ ht = 2 * fpi }} ;             

\ (3)Ellipse function
\ Axis: a = 8 b = 2 rotate: d = 79 degree
\ Positive d value makes rotate CCW
\ display curve has been normalized
\ original point coordinate is at the center of the screen
\ x0, y0 are the principle function value without rotation
\ x, y are the function value after rotate
: f(t)
{{ a = 8 }} {{ b = 2 }} {{ d = 79 }}
{{ x0 = a * cos ( t ) }} {{ y0 = b * sin ( t ) }}
{{ r = d * fpi / 180 }}
{{ x = x0 * cos r - y0 * sin r }} {{ y = y0 * cos r + x0 * sin r }}
;

\ For normalized plotting using
: xyEvaluate BASIC
10 REM xy evaluate
20 LET { t = lt }
30 LET { dt = ( ht - lt ) / I>R ( N ) }
40 FOR I = 0 TO N
50 LET { t = t + dt }
60 RUN f(t)
70 LET { XX ( I ) = x } :: { YY ( I ) = y }
80 NEXT I
90 END ;

\ For normalized plotting using
: XYmaxFind BASIC
10 REM XYmax find out
20 LET { XYmax = XX ( 0 ) }
30 FOR I = 0 TO N
40 LET { XYmax = MAX ( XYmax XX ( I ) ) }
50 LET { XYmax = MAX ( XYmax YY ( I ) ) }
50 NEXT I
60 END ;

\ Store floating point value into the array
: CartesianConvert BASIC
10 REM polar to cartesian convert
20 FOR I = 0 TO N
30 LET { XX ( I ) = 250 + ( XX ( I ) * 200 / XYmax ) }
    :: { YY ( I ) = 250 - ( YY ( I ) * 200 / XYmax ) }
40 NEXT I
50 END ;

: DataPreparation ( -- )
  init
  XYEvaluate
  XYmaxFind
  CartesianConvert
;

:OBJECT EllipsePlot

<SUPER WINDOW

:M STARTPOS:  400 100 ;M
:M STARTSIZE: 500 500 ;M
:M WINDOWTITLE:
 Z" ABC FORTH Designer: Ching Tang Tseng (C)2015 " ;M 

:M ON_PAINT:

BASIC

300 REM text print

330 RUN 490 251 S" x" TEXTOUT: DC
340 RUN 251   1 S" y" TEXTOUT: DC
350 RUN   1 460 S" R(max)    = " TEXTOUT: DC
360 RUN   1 480 S" scale/div = " TEXTOUT: DC

400 REM XYmax, scale/division floating point values print
410 RUN 80 460 XYmax (FG.) TEXTOUT: DC
420 LET { S/D = XYmax / 4 }
430 RUN 80 480   S/D (FG.) TEXTOUT: DC


500 REM x axis and y axis plot
510 RUN BLACK LINECOLOR: DC
520 RUN 250   0  MOVETO: DC
530 RUN 250 500  LINETO: DC
540 RUN   0 250  MOVETO: DC
550 RUN 500 250  LINETO: DC

600 REM axis scale marks plot
610 FOR I = 50 TO 450 STEP 50
620 RUN 250  I MOVETO: DC
630 RUN 260  I LINETO: DC
640 RUN  I 250 MOVETO: DC
650 RUN  I 240 LINETO: DC
660 NEXT I

\ Change the value type from floating point to integer while plotting
900 REM function curve plot
910 RUN LTRED LINECOLOR: DC
920 LET XI = INT ( XX ( 0 ) ) :: YI = INT ( YY ( 0 ) )
930 RUN XI YI MOVETO: DC
940 FOR I = 1 TO N
950 LET XI = INT ( XX ( I ) ) :: YI = INT ( YY ( I ) )
960 IF ( XI < 49 ) OR ( XI > 451 ) OR ( YI < 49 ) OR ( YI > 451 ) THEN 980

\ For line curve
\ 970 RUN XI YI LINETO: DC
\ 970 RUN XI YI BLACK SETPIXEL: DC

\ For big dot curve
970 RUN LTRED BRUSHCOLOR: DC
972 RUN XI YI  2 FILLCIRCLE: DC

980 NEXT I

1000 END

[ CLASSES ]

;M

;OBJECT

: MAIN
  DataPreparation
  START: EllipsePlot ;

MAIN


2026年1月15日 星期四

線性組合

線性組合


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


在大學三年級以上的高等數學課程中,經常可以聽到老師在教學課堂上強調:解方程式時『解的線性組合亦為其解』。
以前在學時,只是把他背下來,沒有去深入了解,後來走進了社會,經常看到『線性組合』的術語,就開始根據孔子所說的『思而不學則罔,學而不思則殆』之道理來想問題。
結果發現,運用幼年還沒有 x, y 代數知識時期的思考方式想問題,最容易了解,而且要把問題反過來表達,才更清楚。
那時,若問 3 乘以多少能得到 6 時,這個在等號左邊的『乘以多少』移到等號右邊,就應該是被 6『除以多少』了。

同理,我就以同樣的方式思考何謂『線性組合』?與可以運用在那裡?的問題。

根據定義,線性函數有兩個重要特點,以數學式表示則為:

cf(x)= f(cx)
f(x+y)=f(x)+f(y)

其中,『乘以 c 』也隱含了可以『除以 c 』的意義,『加上』也隱含了可以為『減掉』的意義。

思考的時候,就用幼學技巧來思考,例如:

假設已經知道 x = 5 。那麼,『線性組合』類比上的發揮就可以這樣的式子表示:


 

2 * x = 2 * 5 ==> 2x = 10
3 * x = 3 * 5 ==> 3x = 15
2x + 3x = 2 * 5 + 3 * 5 ==> 5x = 25



顯示的結果就是:『線性組合』攪來攪去,實質意義上,卻仍然只是 x = 5 而已 ,性質不變。


這樣子去了解『線性組合』,以後在用到時,就不會懷疑它所代表的實用意義了。
舉例而言:

1. 我們在解矩陣的線性聯立方程式時,也是憑此『線性組合』的意義來進行演算的:某列乘上一個常數加到另一列去,矩陣的性質不變,為什麼?意義就是上述內容。

2. 我們在解所有的方程式時,都要牢記解的線性組合仍為其解來寫通解,其意義也在這裡。韓信點兵的問題,求出第一個最小正整數解後,還得照線性組合的特性來寫出所有整個序列的通解,才是正確的解答。

3. 我們在將循環小數化為可用兩個最精簡整數相除來表示時,求解的方法,也是利用線性組合的意義來求解。

4. 傅立葉級數的展開式,尤其表現了『解的線性組合仍為其解』的物理意義,術語上稱為線性疊加效應。

5. 人臉辨識系統的數學運算,也就是將整個圖形的影像數據,進行線性組合式的運算,來獲得 zoom in/zoom out 的效果,以便與基本庫存圖檔的設定尺寸吻合後,才進行比對,落定結果。

6. 斷層掃描得到的訊息,都是以 180 度半圈掃描測量所得形成的數據,每一點的內容,都是沿著徑向相關點之訊息的線性組合。

7. 量子電腦儲存訊息的技術,在運用時,更發揮了線性組合的觀念,才更能以三度空間的方式,儲存大量的訊息。

8. 相控陣列感測系統,無論是聲納或雷達,也是全靠線性組合的原理處理訊號,將所有感測到的訊息,快速算出線性組合而成的輪廓花樣,就能得到放大了陣列維度倍數的精密訊息。

最原始武器的陣列感測器系統,純用類比硬體製造,解析度就難以提高。後來,改採數位電腦處裡訊號時,卻遇到非即時性處理,運算速度不夠快,難以即時得到結果的困擾。 以後,利用量子電腦,可以利用大位元數量操作的優點,與量子態的感應式光速運算能力,相控陣列雷達的性能,就能藉著量子電腦達到大躍進式的性能提升,用到的最基本原理,仍然就是線性組合。


『線性組合』運用在無數的地方,只是大家沒去注意而已。

我在了解線性組合的科學意義後,做過延伸性的思考,自問『乘以一個常數還能相加』的特性,在複數體系中是否照樣有效?若想深入探討這個問題,就必須擁有能夠很方便於操作出複數運算的系統,於是自行設計出它來。
我所設計的數學運算體系,都能辦到這樣的功能,運算程式可以直接寫成使用複數作為基本運算單元的格式。

學校課堂上的老師,不會採用複數運算教學的方式來驗證各種理論給學生看,否則整堂課,可能只能教一題複數運算的題目。

我在知名國際論壇上發表過這方面的研究成果,並公開展示,當時,沒有想要丑表自己先進的念頭。卻在不久之後,發現有人刻意趁機捐獻這種程式給公開的相關網站,博取名聲。我很不以為然,但是沒有生氣而指責對方的這種行為,因為他也是論壇中來往過的對象,自己知道他會這樣做,以後小心便可。


線性組合原理,其實是大自然運行的法則,包括人類必須順應天理的優良人文思想,也是如此。

中華文化,強調大家應該遵守儒家的人倫觀念,繁延我們的子孫。這就是最正常、最適當的線性組合理念,能讓整個社會與民族,健康、安定的發展下去。一個爸爸結合一個媽媽建立一個健康的家庭,養育出健康的下一代,是再美好不過的事情。如果有人硬是要搞基因工程,強將兩個爸爸與一個媽媽的基因組合在一起,結果可能會生出有兩個腦袋的畸形連體嬰來,很難養活。


這裡有部影片,拍攝到很難得的兩隻火鶴同時餵養幼鶴的寶貴鏡頭。
影片看起來很血腥,好像是火鶴爸爸,凶狠的啄穿了火鶴媽媽的腦袋,流出腥紅的血乳,餵養著還不能覓食的幼鶴,吃完一頓血乳,還弄得滿身都是血。
其實,火鶴不是哺乳動物,火鶴沒有那麼殘暴,幼鶴也沒有那麼狠毒,他門仍然是按照一般禽鳥類動物餵養幼兒的方式,採取反芻出大量已消化的營養物來餵食幼鶴。火鶴消化作物類食物來吸收養份的方式,有點特別,牠們喜吃作物類的植物,消化成帶有大量 beta 胡蘿菠素火紅色的養份後,才吸收。反芻出來的營養物,等同於是禽鳥的作物奶,跟人類可用米漿育嬰是同樣性質的東西,不是血奶。
一般而言,一隻單親成鶴,就能養育幼鶴。這部影片則攝得了寶貴的鏡頭,由兩隻火鶴以線性組合的方式,這樣子餵養幼鶴,幼鶴長大了必定更為健康,就是因為他曾享受過順應天理之『線性組合』式的栽培。

2026年1月2日 星期五

數字轉換成數值

數字轉換成數值


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


數學計算是電腦的主要功能之一,數學計算程式則處理數值問題。數值在被處理成電腦可以執行運算的格式前,僅只是人們可以認得的數字,例如:口語上的一百二十三,寫成人們可以認得的阿拉伯數字時是123,它只是一個數字,一般電腦系統,通常均只將其識別成『字』而不是可以用來運算的『值』。因此,凡涉及數學計算的程式系統,都必須具備將數字轉換成數值的指令,反之,為了顯示運算結果,也必須具備將數值轉換成數字的指令,電腦才能接受人們設計的數學計算程式,執行程式內指定之數學運算,最後,以數字表示方式顯示結果。

近代FORTH,特別是依循ANSI標準設計出來的系統,常令使用者在設計將數字轉換成數值的程式時感到困擾,問題的起因,就在一個設計轉換程式時,常會用到的關鍵性指令:>NUMBER,其標準規格,似乎是被訂定成強調數學計算功能者所不願見到的格式了。

首先,它不處理帶有負號的輸入數字,數學計算怎麼可能不管負數?這個標準原生指令則確實不管,因此,設計數學計算系統者,只好自行在系統中,增加設計補其功能不足的程式。

另外,傳統非ANSI標準的FORTH系統,在處理數字輸入的方式上,通常就將帶有小數點格式的輸入數字,列入等同於>NUMBER之指令亦應處理的對象之一,這種功能,甚至於被設計成隱性的處理方式,讓系統自動記錄,輸入數字有無小數點?有,則在第幾位位數之處,記錄會被留在一個常被稱為DPL的變數內。

更先進的FORTH系統,則將處理數字輸入方面的整個問題,進行了更具彈性的安排,也就是硬將等同於>NUMBER的基本指令,規劃成執行向量式的指令格式,其執行內容,可隨設計者任意調整。

當初,在議定>NUMBER指令的ANSI規格時,可以想像,必定發生過非常混亂的紛爭,因為,事隔十年之後,還能見到許多公開譴責這個指令標準格式的言論。事實上,想實現包括了上述傳統數字轉換成數值所有功能的指令規格,確實不易訂定,因此,不納入規格,反而更能配合後序的千變萬化程式設計。使用者當然會感到麻煩,負數、小數點都得另行處理,整個FORTH系統運行起來之後,有更多的數字輸入識別處理工作要做,而>NUMBER標準指令,只能提供非常有限的數字轉換成數值之功能。有時,系統乾脆不用,僅在系統之INTERPRET內單獨設計隱性處理程式,然後,只按ANSI規格設計>NUMBER顯性指令供使用者使用。

執行>NUMBER指令前後的堆疊上參數之表示說明為:

( ud1 addr1 n1 – ud2 addr2 n2 )

其中,ud表正的雙整數,恆為正數,不管負值。addr1 n1則表待轉換數字之起始位址與字長量,addr2 n2則表轉換到不能轉換時的位址與剩餘字長量,小數點將被視為不能轉換的對象。

傳統FORTH中,等同於>NUMBER的指令,就被稱為NUMBER,其對應規格為:

( addr – d )

addr表待轉換數字之起始位址,d為轉換出來的數值,可正可負。轉換到不能轉換的標界,是空白字元,因此,可以接受數字內有小數點,指令會將位數存入DPL變數。

雖然,ANSI中的>NUMBER指令規格,不太令人滿意,並不表示使用者絕對無法實現數字轉換成數值的程式,只是表示想達到目的時,較為麻煩而已。單憑此一ANSI標準規格的>NUMBER指令,我們仍然能以高階定義的方式,定義出傳統之NUMBER指令來。

下列這個程式就能達到目的,但執行內容則不管小數點的問題。


    
   
: NUMBER ( addr -- d )
  0 0 ROT DUP 1+ C@ [CHAR] - = >R
  COUNT R@
  IF 1 /STRING		\ 去除負號佔用過1個字元量
  THEN 				\ 否則不必去除1個字元之量
  >NUMBER 			\ ( ud1 addr1 n1 – ud2 addr2 n2 )
  NIP 0= 			\ drop addr2, no matter n2 is = 0 or <> 0 consume n2
  IF	R>			\ keep ud2, check sign?
  	IF DNEGATE THEN 	\ ud2 ==> -d
  ELSE	R> DROP		\ drop positive sign, ud2 ==> +d
  THEN ;

應用於一個精簡的立即式中算符程式,可以檢驗此NUMBER指令的性能。

forth definitions
vocabulary ikq
ikq definitions

: nexti bl word number drop ;
: + nexti + ;
: - nexti - ;
: * nexti * ;
: / nexti / ;
: = . ;
forth definitions

\ usage:
\ ikq
\ 123 + 456 * 3 / 2 =

上述NUMBER指令的獲得來源,係J. L. Bezemer所著之”And so Forth”中第3.28章節起所使用的範例程式,網頁網址為:

http://thebeez.home.xs4all.nl/ForthPrimer/Forth_primer.html

原始程式列示如下:

S" MAX-N" ENVIRONMENT? \ query environment [IF] \ if successful NEGATE 1- CONSTANT (ERROR) \ create constant (ERROR) [THEN] : number ( a -- n ) 0. Rot dup 1+ c@ [char] - = >r count r@ if 1 /string then >number nip 0= if d>s r> if negate then else r> drop 2drop (error) then ; 將其寫成較佳的具有層次結構格式則為: : number ( a -- n ) 0. Rot dup 1+ c@ [char] - = >r count r@ if 1 /string then >number nip 0= if d>s r> if negate then else r> drop 2drop (error) then ;


J. L. Bezemer可能是德國人,他們喜歡自成一格的將number指令設計成轉換出單整數的規格,因此,堆疊上參數表示方式的格式為:( addr – n ),與傳統的( addr – d )不同,他們有堅持使用這種number的理由。一方面係此number指令中不處理帶有小數點的數字,另方面係未來系統將以64位元為主,一個雙整數單元能放置的數字,實際上相當大,約為38位數,系統中已不再需要強調使用雙整數,18位數的單整數已相當夠用了。

我所修改出來的程式,與原程式比較,只是去掉d>s,並將2drop改為drop,便可得到( addr -- d )的規格。至於原程式中使用到的(error)常數,可以不用。我在測試Sod64 Forth與MinForth兩套系統時,均遭遇到系統只提供>NUMBER新ANSI規格指令,但不提供傳統FORTH中之NUMBER式規格指令的困擾,後來,從SwiftForth系統使用說明中,讀到遇此困擾時的處理辦法,建議使用者,將數字轉移到PAD緩衝區去,再當一般文字來慢慢地仔細處理,感到如此使用FORTH的技巧,非常拙劣。J. L. Bezemer在教材書籍中列示範例程式,採用以>NUMBER來設計出傳統NUMBER的技巧比較高明。

將數字轉換成數值程式,最常使用於有對談式要求的場合,程式要能讓系統停下來,等待使用者輸入訊息,系統獲得輸入訊息後才繼續執行,而輸入的訊息可為文字或數字,但對系統而言,都是『字』而非『值』。

發展系統時,很需要精簡的對談式功能,以利單個指令或簡明程式的測試。我在發展數學計算系統時,更有這種需求,尤其是面對新到手的系統,必須進行簡單指令測試時更甚。測試時,又以輸入為數字者居多,因此,經常要為新系統添加這方面的功能性指令,我常將其定名為GetNumber。

以SwiftForth為例,說明資料中建議的設計方法為將輸入數字全送到PAD緩衝區去處理:

: input ( -- n )
PAD 5 BLANK PAD 5 ACCEPT >R
0. PAD R> >NUMBER 2DROP DROP ;

其中兩個相關指令的堆疊參數表示方式規格為:

>NUMBER ( ud1 addr1 n1 – ud2 addr2 n2 )

ACCEPT ( addr n1 – n2 )

SwiftForth系統中仍提供NUMBER指令,但其規格為( addr c – n ) 而非 ( addr – d ),與一般FORTH系統不同。而且,其WORD的指令規格,亦與別的系統不同。一般FORTH系統,執行過WORD指令後,堆疊上會自動得到HERE之值,但SwiftForth之WORD不留HERE之值在堆疊上。

在Win32Forth系統中處理數字輸入的典型設計則為:

: GetNumber ( -- n )
CR QUERY BL WORD NUMBER D>S ; \ DROP會棄除負號,故只能用D>S。
: GetFloat ( -- float )
CR QUERY BL WORD COUNT >FLOAT DROP ;

我曾在Win32Forth中設計一次可以輸入兩個數字的程式,記錄如下:

\ : QUERY TIB 80 ACCEPT #TIB ! 0 >IN ! ;
: Get2Numbers ( -- n n )
CR QUERY
BL WORD NUMBER D>S
BL WORD NUMBER D>S CR ;

F83的設計格式則為:

: GetNumber ( -- n )
CR QUERY
BL WORD COUNT NUBER CR ;

在Sod64 Forth系統中,則有NUMBER?指令可用,其規格為( c-addr – d f ),c-addr表打包規格的字串,首個位元組(Byte)要放字長數量,轉換出雙整數d,並留下旗號f。因此,測試程式為:

: Get2d ( -- d d )
BL WORD NUMBER? DROP
BL WORD NUMBER? DROP ;
: Test ( -- )
Get2d D+ D. ;

類似Sod64 Forth或MinForth的系統,均為只能在文字模式( text mode)下操作的系統,其QUERY藉KEY指令設計而成之後,不能以一般系統之使用方式實現對談式輸入要求,必須改以類似SwiftForth說明中的建議,全搬到PAD緩衝區去後再行處理。

MinForth為純ANSI系統,只能在純文字模式狀況下使用,下例可用:

: GetNumber ( -- n )
query 0.
bl word count over c@ 45 =
if -1 >r 1- swap 1+ swap else 0 >r then \ sign > r
>number 2drop r> if dnegate then d>s ;

MinForth中亦有convert指令,顯然也只能操作出ud,而不管負數。指令若不具有處理帶有小數點數字字串的功能,一旦數字出現小數點時,這種數字便只能被處理到該小數點為止的位置,因此,這個系統的數字輸入格式,只能為純整數。

負號因恆在數字前首,尚易於設計處理帶有負號之輸入數字的程式,若想再添加也能處理帶有小數點之輸入數字的功能,將其納入程式,則程式必定複雜。

啟用MinForth系統時,進行各種數學計算所需單一指令之測試,確實被對談式輸入數字的問題困擾許久。後來,思考到所有FORTH系統自身運轉起來之後,豈不是都能自行接受任何輸入數字嗎?使用者何不借力使力,就用系統已經存在的功能,完成處理輸入數字的應有工作,使用者何須還得另外設計程式?於是測試出叫用系統天王指令INTERPRET來協助完成需求功能的程式,如下:

\ Naughty code for the purpose of interactive input numbers.
: GetNumbers ( -- n/d/f ..... )
POSTPONE [ QUERY INTERPRET POSTPONE ] ;

將這個GetNumbers指令應用在對談式輸入數字的程式中時,輸入就可以混合著輸入整數、雙整數、浮點數,輸入數字的個數及秩序均無限制,都能被系統自動轉換完成後,依序放在堆疊上,以供後續指令使用。

GetNumbers指令的設計原理,運用了FORTH系統能夠運轉起來的根本機制,程式中見不到惱人的NUMBER指令,數字自動轉換的工作,是隱性的潛伏在INTERPRET指令之中了。

從Julian V. Noble所著的A Beginner’s Guide to Forth書中可以見到下列用來描述FORTH系統內所謂之外執譯程式( outer interpreter)執行時的封閉流程,這個流程就是FORTH天王指令INTERPRET功能程式的主要內容,其中,將數字轉換成數值,並將其放置在堆疊上的部份,就是我利用來設計GetNumbers的根據。

The diagram below is a flow chart representing the actions performed
by the Forth outer interpreter during interpretation.


A continuous loop waits for input—from the keyboard, a disk file or
whatever— and acts on it according to its nature. Input consists of a sequence of words and numbers. If a name is recognized it is executed; if it is not in the dictionary (where else would you keep a list of words?) Forth tries to convert it to a number and push it on the stack. If this is impossible, Forth aborts execution, issues an error message and waits for more input.

以上從網上節錄下來的資料,明確的說明了我利用INTERPRET來設計出GetNumbers指令的根本原理,資料出處的網址為:

http://galileo.phys.virginia.edu/classes/551.jvn.fall01/primer.htm

INTERPRET雖不是一個標準指令,但所有的FORTH系統內都有,善加利用,可以設計出許多非凡的指令來。此前,我曾利用它來設計過斷點式的除錯程式,其功能被附加在ABC FORTH系統的PAUSE指令中了。後來,我又發現了利用這個INTERPRET指令便能精簡輕易的從BASIC式程式格式中叫用FORTH指令的特異用法,結果就設計出了ABC FORTH中的RUN指令。這次再度利用INTERPRET設計出GetNumbers指令,算是第三度的精彩運用了INTERPRET。

典型的使用範例如下:


 
\ 整數的質因數分解
: factors ( n -- )
2
begin  2dup dup * >=
while  2dup /mod swap
       if   drop  1+ 1 or             \ next odd number
       else -rot nip  dup . ." * "
       then
repeat
drop . ;

: main ( -- )
cr ." Please enter an integer number: "
cr GetNumbers
cr dup . ." = " Factors
cr ;

執行結果:

main 
Please enter an integer number: 
987654328
987654328 = 2 * 2 * 2 * 123456791 
ok



我在設計ABC FORTH系統的早期階段,便設計出系統在處理實數的環境中,能夠主動將三種數字自動處理成浮點數。純整數、帶小數點數、浮點數三種數字,是使用者可能用到之所有的表示數字,若欲令系統能將三者均自動轉換成浮點數,則必須動手修改天王指令INTERPRET的執行內容,才易於實現要求。幸好,Win32Forth系統中,已將INTERPRET內所需要之相關於處理數字指令(NUMBER),規劃成一個可以改變執行內容的執行向量式指令了,我只須在完成設計後,以轉換向量內容方式,切換指令指向之位址便可。

上述GetNumbers指令雖頗具使用彈性,但得注意其可用規格,它只適合在對談式應用場合,於系統停頓下來後,純粹等待接受使用者只能輸入數字時使用,不適合處理文字,或文字以外另有後續指令的場合。例如:上述以ikq字彙讓系統能執行中算符的應用簡例中,因使用者除了輸入數字外,還可能也要連續輸入指令,這樣的應用,便不適合採用GetNumbers。

相關於將數字轉換成數值的NUMBER指令,在可自動反編譯的FORTH系統中,或較有用的FORTH書籍中,均可找到此一指令的源程式,因ANSI的新規格性能欠佳,使用者便可能常需要自行設計將其改善的新功能程式。因此,所有可獲得之有關NUMBER指令的源程式,便成為可貴的資源,特將屬於書籍方面的資料來源彙整於此,留供日後參考,新資料亦可添加於此。

 

(1). Dr. C. H. Ting, “eForth and Zen”, Offete Enterprises, Inc. 1993, p.64e. NUMBER? ( a – n T, a F ) (2). Dr. C. H. Ting, “Technical reference manual”, Offete Enterprises, Inc. 1989, p.83 (NUMBER?) ( a – d f ) (3). Dr. C. H. Ting, “System guide to Fig-Forth”, Offete Enterprises, Inc. 1981, p.88 NUMBER ( a – d ) (4). Leo Brodie, “Starting FORTH”, FORTH, Inc. 1981, p.279 NUMBER ( a – n/d ) (5). Glen B. Haydon, “All About FORTH”, Haydon Enterprises, 1982, p.65 ( a – d ) (6). Mitch Derick & Linda Baker, “FORTH Encyclopedia”, Forth Interest Group, 1982, p.240 NUMBER ( a – d ) (7). Leo J. Scanlon, “Forth programming”, Howard W. Sams & Co., Inc. 1982, p.180 NUMBER ( a – d ) (8). Martin Tracy, Anita Anderson, Advanced MicroMotion, Inc, “Mastering FORTH”, 1989, Brady books, a division of Simon & Schuster, Inc. p.132 VAL ( a n – d f | 0 ) (9). Mahlon G. Kelly, Nicholas Spies, “FORTH: a text and reference”, Prentice-Hall Englewood Cliffs, N.J. 07632, 1986, p.195 NUMBER ( a – d ) NUMBER ( a – d or n )