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


沒有留言: