2025年11月16日 星期日

FORTH 系統的今昔

FORTH 系統的今昔


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


從 1960 年開始,我已經接觸電腦相關技術 60 年以上。
從 1980 年接觸單板電腦 AIM-65 ,開始正式進入 Forth 的領域,已經 45 年了。從那時起,我只專心於使用 Forth,不再分心搞其他東西。

一直到 32 位元的 Win32Forth 系統被廣泛使用後,我才再把注意力集中於發展出完整的 ABCForth 數學計算系統上。此前,在 16 位元的時代,我所設計的 ABCForth 系統因缺乏好用的浮點算術功能,有效位數不足,不適合用來執行數學計算程式。出現適合使用的浮點運算,已是 2000 年以後的事情。我在使用十幾年 Win32Forth 系統後,在 2010 年的筆記本上記錄過一個 Forth 系統中現行功能的分類,如下列所示:

1. 核心基本程式(kernel)
2. 組合語言程式(Assembler)
3. 蛻變編譯程式(Meta compiler)
4. 編輯程式(Editor)
5. 除錯程式(Debugger)
6. 多工程式(Multitasker)
7. 檔案操作程式(File operation)
8. 浮點運算程式(Floating point operation)
9. 繪圖程式(Graphic display tools)
10. 語音輸出程式(Sound output tools)
11. 音樂輸出程式(Music output tools)
12. 物件導向驅動程式(Object oriented driver)
13. 動態連結界面程式(Dynamic linker library tools)
14. 作業系統界面程式(Operating system calling)
15. 系統內多人共用程式(Multi-user program)
16. 標的系統產製程式(Target generator)
17. 資料處理程式(Data base program)
18. 平行處理程式(Parallel processor)
19. 網路自動傳輸程式(Internet Autotransfer)
20. 科學程式庫界面程式(Scintific library interface tools)
21. 外殼語言控制程式(Shell language controller)

其他出現過的零碎功能程式則不在此列。這樣的表列不同於標準指令的分類,上述各類程式也全是網上古聖先賢捐獻出來過的開放式公益程式,我都享受過、使用過、從中體會過它們的用途與性能。很多程式都不僅是只有一套,可能有很多套,全都來自名家的設計,使用者只能感謝他們的貢獻。

大約是十五年前,我開始改用 Linux 作業系統,主用之 Forth 系統從 Win32Forth 系統換成了 ciForth 系統。發展軟體技術更換跑道是一件痛苦的事情,但是隨著時代的演變,這卻是不得不為之的工作。這十多年來,我繼續開發我有興趣的 ABC Forth 數學計算系統,許多成果已經發佈在這個個人網頁上。

長期的發展經驗告訴我,上述曾經有過的這許多功能程式,有些必須更迭了,有些則是不太需要發展,而是必須設法去適應新環境的新用法。我們就按照編號秩序逐個檢討,沒變化的序號就略過不談。

1.從略。

2.組合語言程式,在我使用 ciForth 後,不再在書寫 Forth 程式時需要使用。由於 ciForth 系統本身的組合語言源程式完全公開,需要動用到組合語言功能時,可以在長成系統前,直接使用組合語言自己設計,這樣的用法與習慣與過去不同。系統的作者曾經打算提供在 Forth 系統內的組合語言功能,後來放棄了。

3.蛻變編譯程式,在 ciForth 系統中已不需要,新採用的方法則是單憑一個 save-system 指令完成將發展成果固定下來的操作。這樣的理念非常好,只可惜作者的努力畢竟還是有限的,某些作業系統下的 ciForth 不能完成 save-system 的工作。在我使用之 Ubuntu Linux式作業系統中均無問題。

4.編輯程式在 64 位元的環境中已經不再設計,所有的編輯工作都配合作業系統提供的純文字編輯器(text editor)來寫出檔案程式,然後交給 Forth 系統編譯後執行出結果,這已是流行用法,至於編寫出來的檔案可能在經由網路遙控操作時無法工作之問題,不在 Forth 系統需求討論之列,此處不討論。 ciForth 系統另有個特色,作者將一些可以選擇性載入的功能程式放在一個叫作 forth.lab 的檔案內,使用者有需要時可以自行使用一個名為 wanted 的指令取得功能程式。 forth.lab 程式的內容是採用傳統之幕區塊(Block)方式設計出來的配屬資源,增改其內容必須使用傳統的幕式編輯器(Block screen editor)才能操作,系統不提供,使用者只能操作幕式顯示指令(n list)來看內容。

5.不討論,因為 Forth 本身就具有多種天生的除錯工具。

6.多工問題已不再在 Forth 系統中被重視,因為作業統大部份都已具備先佔式(Temptative)多工的性能。若 Forth 系統還要設計傳統上的知更鳥式輪唱型多工(Round robin multitask)實無必要。我在使用 ciForth 需要多工時,就透過 ciForth 能操控作業系統的功能直接完成,本網頁貼出過這樣應用的展示範例。

7.從略。

8.本該從略,但因我在使用 ciForth 系統時碰到困難,原始系統沒有提供恰當全套的浮點算術功能,我又有個人需求,只好自己全用軟體設計,已經完成很久而且一直在用了。

9.繪圖程式一直都是所有 Forth 系統發展中的最大障礙,我有種感覺,Forth 系統一旦具有了完整的繪圖功能,系統的發展也就接近尾聲了。 ciForth 系統沒能提供,也許以後會有,但目前沒有。我的變通使用方法,仍然是透過系統具有第 21. 項能操控他種程式語言的方式達到目的。只要該種語言屬於像最流行的 Python 照本宣科式語言(Scripting language), ciForth 都很容易達到目的。

10.語音輸出程式。這方面的功能取決於作業系統可以自由下載的資源而定,套套不同,但也套套都很容易實現,討論從略。

11.音樂輸出程式。與10.的條件相同,討論從略。

12.物件導向驅動程式,這本在 Win32Forth 系統中非常強調的功能,已經被棄用, ciForth 系統中沒有,討論從略。

13.動態連結界面程式,實現它的方式,首先需要取得可動態連結由 C 式語言設計出來的程式。在 Forth 這邊則需要動用到能放置動態程式於記憶體的指令,然後根據動態程式中提供之可通用指令名稱在 Forth 系統中叫用。本網業已貼出過好幾個示範程式,請自行查找。

14.作業系統界面程式。這在 Linux 作業系統中具有完全公開可用的資源,使用時要傳遞幾個參數的叫用方式 ciForth 已設計得非常完善,資源參考資料只能自己在網上查找。

15.多人共用程式,這方面恆是商業販售的軟體,已不在開放式 Forth 系統中被公開討論。

16.標的系統產製程式,狀況與 15. 同,討論從略。

17.資料處理程式。這方面的應用,由於新型的照本宣科式程式語言已有一大堆,包括 Python 也具有這類資源, Forth 已不需要自己設計,運用第 21. 條的外殼語言控制程式就能辦到,討論從略。

18.平行處理程式。以前在 Win32Forth 系統上有過同時使用幾個 CPU 平行跑程式的範例程式。後來在 Linux 中沒有見過,但搞 AI 者常用 Cuda 跑平行處理問題了,它好像已不是 Forth 愛用者有興趣的主題,此處討論從略。

19.網路自動傳輸程式。我在 Win32Forth v.4.2.671版中用過,後來沒再使用,但網上見過許多範例,有興趣者請自行查找。

20.科學程式庫界面程式。這項功能是固定的設計,該界面程式不能通過編譯的 Forth 系統,那就表示此系統的設計很有問題了。 Win32Forth 系統與 ciForth 系統都沒有問題。

21.請參考本網頁曾經刊出過的網文,學會設計方法來使用,它是 Forth 的吸功大法,像 BASH 程式語言一樣,專吸別人的長處來實現夢想,非學會不可。

所有軟體的性能,其實不只是只有這些,將來必定會有新的應用出現。Forth 程式語言功能的可延伸性,都能解決所有的問題。只是作者在下功夫設計出公認需求時,需要很先進、很深厚的軟體設計素養,我們在享受他們的貢獻時,勿忘感謝他們。


附貼兩張網上擷取之 round robin multitask 示意圖,與知更鳥的寫真照片。

2025年11月2日 星期日

­誤差函數的應用

­誤差函數的應用


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


我們觀察實驗以求取數據,再將數據輸入電腦,以數值分析技術處理數據,整個過程中可能會出現多種誤差,實驗觀察時可能出現誤差,以電腦處理數據時也可能出現誤差。在深入討論誤差函數的應用前,先行對相關於誤差的術語,進行基本說明。

1 電腦處理數據時可能出現的誤差

數值分析技術以電腦來處理數據,在計算過程中,可能產生兩種誤差。

1.1 捨位誤差(Round-off error)

是由於受到電腦內部記憶體位元量的使用限制所產生的。一個包含小數位數的十進制數目,將其轉換成等效的二進制數目時,由於電腦內部可用位元數的限制,必然產生一種轉換誤差,尤其是沒有恰恰相等的二進制表示值時,更加顯著。如果執行計算時,所需的位數比電腦或編譯器能貢獻的位數為多時,亦有另一類捨位誤差產生。

1.2 截項誤差(Truncation error)

它是由於電腦只能處理有限項數學表示式的限制所產生的誤差。以數值分析法解數學問題時,若需將數學問題作一合乎工程要求的近似程序後,才能求得其解,所得的結果,並非絕對性的純數學解,近似式的誤差項,電腦程式主動捨去,因此,應用電腦來解數學問題時,常有截項的要求,也因此產生了誤差。另外,在電腦上以數值分析方法算出超越函數的函數值時,因為電腦不能處理超越函數所展開的無窮多個項式,而僅能處理帶有加、減、乘、除有限項組成的近似於超越函數的式子,截項誤差因而產生。

本文不對上述兩種誤差進行更深入的討論。

2 實驗觀察數據可能出現的誤差

這一部份的敘述內容,摘要自中國科學出版社出版的『誤差理論與實驗數據處理』一書,作者為馮師顏先生,取其說明詳盡、恰當而刻意採用,且因一般讀者難於自行查找此書,故不惜篇幅轉錄於此。本章程式中使用的數據,亦源自該書,特此聲明。
誤差是指觀測值與真值之差,偏差則指觀測值與平均值之差。根據誤差的性質及其產生的原因,可將實驗觀測數據時,可能出現的誤差分為四類:

2.1 系統誤差

是指因為
(1)儀器不良:如刻度不準,砝碼未校正。
(2)周圍環境的改變:如外界溫度、壓力、濕度的變化。
(3)個人的習慣與偏向:如讀數常偏高或偏低…等,所引起的誤差。。
此種誤差在同一物理量的測量中為一定,根據儀器的缺點、外界環境條件變化影響的大小、個人的偏向,分別加以校正後,可以去除得掉。

2.2 恆定誤差

是指在測量中未發覺或未確認的因子所引起的誤差,這些因子影響結果,永遠朝向一個方向偏差,其大小及符號在同一實驗中完全相同,檢查或改正的方法是擴大實驗範圍,如採取不同方法選取樣品,使用不同方法提存分離樣品…等。

2.3 偶然誤差

在測量中,如果已經消除引起系統誤差的一切因素,而所測量的數字,仍在末一位或末二位數字上有所差別時,我們就稱這種誤差為偶然誤差。偶然誤差有時大、有時小、有時正、有時負,方向不一定。偶然誤差產生的原因,屬於大自然的統計現象,因而也就無法控制,但用同一精密儀器,在同樣條件下,對同一物理量作多次測量,若測量次數足夠多,則可發現偶然誤差完全服從統計上的定律,誤差的大小、以及正負誤差的出現,完全由機率決定,我們沒有理由認為誤差偏向一方比偏向另一方更為可能。因此,誤差與測量的次數有關,隨著測量次數的增加,偶然誤差的算術平均值將更接近於真值。偶然誤差的發生,完全出於偶然,受或然率所支配,因此,偶然誤差可以用或然率理論來處理。

2.4 過失誤差

是一種顯然與事實不符的誤差,它主要是由於粗心大意、過度疲勞、或操作不正確所引起,例如:讀錯刻度值、反讀游標尺、記錄錯誤、計算錯誤…等,此類誤差無規則可循,只要多方警惕,細心操作,過失誤差就可避免。

本文內容針對最後兩項:偶然誤差與過失誤差進行討論,作為應用誤差函數來分析數據的實例,亦即利用統計學理論來分析數據,找出數據中的過失誤差值。而這樣的應用,卻是廣泛存在於工程、研究、實驗、生產…等方面,最為需要的基本分析技術。

範例程式可以直接、實際應用於各種場合,並突破傳統的分析方法,不靠查表,直接算出實際數據,獲得明確的數字解答。因此,可以不必採用傳統上模糊的敘述當作結論。傳統分析方法,通常都僅稱『遠大於或遠小於某值』來代表分析結果。

3 誤差分佈函數

高斯(Gauss)於 1795 年找出了誤差分佈函數,以其繪出的曲線稱為高斯正態誤差分佈曲線,並建立了所謂的高斯誤差定律,其特點如下:
(1) 小誤差比大誤差出現的機會多,故誤差的機率與誤差的大小有關。
(2) 大小相等,符號相反之正負誤差的數目近於相等,故機率曲線對稱於y軸。
(3) 極大的正誤差與負誤差出現的機率非常小,換句話說,就是大誤差一般不會出現。

函數圖形繪如 Fig. 1 所示:

高斯誤差函數表示如下:


(3.1)
Fig. 1 高斯正態誤差分佈函數曲線圖

圖中 y 方向的最高點與標準誤差 𝝈 成反比,標準誤差愈小時,曲線中部升得愈高,兩旁下降愈快,因此,標準誤差可以用來決定誤差分佈曲線幅度的大小。另外,標準誤差還能定出誤差分佈曲線的轉折點,上圖中,曲線中部曲率向下,曲線兩端曲率向上,因此,曲線上必有轉折點,根據微積分原理,在轉折點上曲線斜率的變率為 0 ,而曲線上的轉折點就在 x = ∓ 𝝈 處。
針對同一事件的測量,如果各組數據計算出來的標準誤差明顯不同時,那麼,誤差分佈曲線相互比較的結果,就會如 Fig. 2 所示:
Fig. 2 三種不同大小標準誤差的誤差分佈函數曲線關係

由 Fig. 2 圖中可以看出,一組測量數據的標準誤差愈小,表示這一組測量數據的品質愈好,因為數據都分佈在中央平均值附近。反之,如果一組測量數據的標準誤差愈大,表示這一組測量結果品質欠佳,因為有較多的數據,離中央均值較遠。

4 誤差函數(Error function)

誤差函數指的是在誤差分佈函數中,小於某一誤差值的或然率,通常以 erf(t) 表之,其中的 t 即該指定誤差值。因此,誤差函數必須由前一節所敘述的誤差分佈函數曲線下方的積分面積來獲得,不幸,這是一個無法經由手算獲得其值的積分式子,通常以下式表示(積分範圍從-t積分到+t):


(4.1)



但上式中的 t 值為直接計算出來的數值,若以 X 表測量值, AVG 表平均值,而標準誤差為 𝝈 時,這幾個數值間的關係式為:



(4.2)





我們可以從所有的測量數據 X 直接計算出平均值 AVG 及標準誤差 𝝈 ,然後根據 (4.2) 式算出 t 值。現在先行假設,我們能夠經由 (4.1) 式獲得 erf(t) 之值,然後探討此值代表的實際意義。
erf(t) 值代表一個或然率,它的意義是小於 t 值以下的或然率,若以 1 - erf(t) 表示,則代表 t 範圍以外的或然率,因此,前人就利用這樣的規則來重新表達測量數據之誤差,落於指定範圍以外的或然率為多少?
如果我們再以標準誤差為基準,說明某一測量值之誤差,相當於多少個標準誤差之值時,落於範圍以外的或然率為多少?可以讓觀念簡化。因此,產生了一些分析術語,而術語中所敘述的數字,全都是經由計算 1 - erf(t) 函數後所得的數值,我們先將與這些術語相關的數值列成下表。

表中第一行,表 K 個標準誤差量,而 K = | X - AVG | / 𝝈 。
表中第二行,表對應的或然率 P(K𝝈),其值由 1 - erf(t) 函數計算而得。

表1. K 個標準誤差及其或然率值


K𝝈		P(K𝝈)
0		1.0	
0.6745		0.5
1		0.3173	
1.6449		0.1000
1.9600		0.0500


誤差量為 0 個標準誤差時,落於此誤差量以外的或然率是 1.0 ,也就是 100%。
誤差量為 0.6745個 標準誤差時,落於此誤差量以外的或然率是 0.5 ,也就是 50% ,它有一個特別的名稱,叫作或然誤差(Propable error)。
誤差量為 1 個標準誤差時,落於標準誤差範圍以外的或然率是 0.3173,也就是 31.73%。
誤差量為 1.6449 個標準誤差時,落於此誤差量以外的或然率是 0.100 ,也就是 10% ,它也有一個特別的名稱,叫作 9/10 誤差(Nine-tenths error),有 1/10 在範圍外。
誤差量在 1.96 個標準誤差時,落於此誤差量以外的或然率是 0.0500 ,也就是只有 5% ,特別名稱則為 95% 誤差(Ninety-five-hundredths error),只有 5% 在範圍外。

這些術語在電腦應用不太發達的時代,可以強化觀念,以便認識數據的分析結論。今天,幾乎人人都可以非常方便的使用電腦了,我們能夠進一步直接計算出上述所有術語中使用的精確數字。敘述方式便可以更直接了當的改述如下:

每個測量數據都可以落於0個標準誤差範圍以外。
誤差落於或然誤差以外的可能性,兩次就可能出現一次,因為或然率為 50%,100/50=2。
誤差落於標準誤差以外的可能性,三次左右可能出現一次,因為或然率為 31.73%,100/31.73=3.1516。
誤差落於 9/10 誤差以外的可能性,十次才可能出現一次,因為或然率為 10%,100/10=10。
誤差落於 95% 誤差以外的可能行,二十次才有可能出現一次,因為或然率為 5 %,100/5=20。

如此的敘述,將被用來設計我們的數據分析程式,至於幾次才可能出現一次的觀念,則完全可以由電腦直接計算後,合理的印出於輸出報告中,程式就會顯得更為友善與清楚了。

5 erf(t) 函數近似計算程式

我於 2010 年,曾經參與 Fig 總會科學程式庫 #62 Error functions 公益程式公佈前的審核工作,因此收集過不少誤差函數的相關資料,也仔細執行過該程式,知道程式的性能,主要在強調計算所得具有小數點後面將近12位數以上的精確度,程式中也有一個只有大約四位數精確度的附帶程式,以便使用者可以快速取得誤差函數的約略值。
本章由作者自己設計的程式,放棄上述 #62 中的兩個程式,因為第一個太精確了卻計算冗長、程式量龐大,第二個又太精簡了,不如一般書籍中所建之誤差函數表。我在審核程式的過程中,必須花不少錢,才能獲得審核所需的原始論文,因此,耗費不少時間尋求不用花錢的可用資源,後來收集到一個能有 1.5E-5 精確度的有理近似式,現在就列示於此,然後使用於本文的範例程式中。
近似計算的數學式子如下:

其中 t = 1 / ( 1 + pz ) ,而 epsilon 表截項誤差量,所用到的係數如下:
資料出處,為 J. M. Smith 所著之 Scientific analysis on the pocket calculator, Chapter.4 。


 
erf(z) = 1 - [ t ( a1 + t ( a2 + t ( a3 + t ( a4 + a5 t ) ) ) ) exp ( -x ^ 2 ) ] + epsilon(z)

p   =   0.3275911
a1  =   0.254829592
a2  =  -0.284496736
a3  =   1.421413741
a4  =  -1.453152027
a5  =   1.061405429


6 數據分析程式

\ 數據分析程式 \ 分光儀測量鋁錠中鐵含量(%)十次,分析這十個測試數據是否合理的程式。 \ 作者:曾慶潭 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 ) = 1.52 }} \ (1)輸入待分析數據。 {{ DATA ( 2 ) = 1.46 }} {{ DATA ( 3 ) = 1.61 }} {{ DATA ( 4 ) = 1.54 }} {{ DATA ( 5 ) = 1.55 }} {{ DATA ( 6 ) = 1.49 }} {{ DATA ( 7 ) = 1.68 }} {{ DATA ( 8 ) = 1.46 }} {{ DATA ( 9 ) = 1.83 }} {{ DATA ( 10 ) = 1.50 }} [[ TOTAL = 10 ]] \ (2)數據的總量為10。 ; : RESET-DATA BASIC 10 FOR I = 1 TO SIZE 20 LET { DATA ( I ) = 0 } 30 NEXT I 40 LET TOTAL = 0 50 END ; : AVERAGE BASIC 10 LET { AVG = 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 } 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 ) ) } 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 / ( 1 + 0.3275911 * AUX ) } 30 LET { D = EXP ( NEGATE ( erfx * erfx ) ) } 40 LET { erfy = 1 - D * T * ( 0.254829592 + T * ( -0.284496736 + T * ( 1.421413741 + T * ( -1.453152027 + 1.061405429 * T ) ) ) ) } 50 IF { erfx >= 0 } THEN 70 60 LET { erfy = NEGATE erfy } 70 END ; REAL TIMES INTEGER DISC INTEGER FLAG : MAIN BASIC 10 REM 20 REM 數據分析程式 30 REM 40 LET FLAG = 0 40 RUN REPORT 50 FOR I = 1 TO TOTAL 60 LET { erfx = ABS ( DATA ( I ) - AVG ) / ( SQRT ( 2 ) * SIGMA ) } 70 RUN ERROR-FUNCTION 80 LET { TIMES = 1 / ( 1 - erfy ) } 90 LET DISC = INT ( TIMES ) 100 IF DISC > TOTAL THEN 120 110 GOTO 160 120 LET FLAG = FLAG + 1 130 PRINT " 統計分析顯示,數據DATA( " ; I ; " ) = " ; { DATA ( I ) ; " 有問題。 " } 140 PRINT " 此值之誤差量,應該是 " ; DISC ; " 次才有可能出現一次。 " 142 print " 1-erfy = " ; { 1 - erfy } 144 print " (data(I)-avg)/sigma = " ; { ( DATA ( I ) - AVG ) / SIGMA } 150 PRINT " 但是這次測量, " ; TOTAL ; " 次就出現一次了,故有問題。" 160 NEXT I 170 IF FLAG <> 0 THEN 190 180 PRINT " 根據誤差函數核算結果顯示,所有數據的誤差量均在合理範圍之內。 " 190 END ;


載入程式後,執行 MAIN 指令,得到下列結果:

MAIN

平均值(Average value) = 1.564000000

標準誤差(Standard deviation) = .1155854662

統計分析顯示,數據 DATA( 9 ) = 1.830000000 有問題。
此值之誤差量,應該是 46 次才有可能出現一次。
1-erfy = 0.0213730758
(data(I)-avg)/sigma = 2.301327396
但是這次測量, 10 次就出現一次了,故有問題。

印出的結果中, 1/0.0213730758 = 46.79291207,這就是報告中 46 次的計算來源。幾十年前,大學生學習統計學,將習得的誤差理論知識,應用於實驗數據分析時,可以經由冗長的手算獲得標準誤差,再算出各個數據與平均值的誤差量,然後計算各個誤差量相當於多少個標準誤差,最後才根據第 4 節中的幾個術語,判斷實驗數據是否合理?所得到的結論是敘述式的:『遠大於某一誤差量』。現今的電腦,可以快速完成冗長的計算,並得到明確的數據,直接說明結果。
根據上列程式印出的報告,我們還能再度利用電腦快速繪圖的功能,以同樣的正態誤差分佈函數曲線圖下方的積分面積,說明分析的結論。Fig. 3 圖中右邊曲線下方的陰影區,即代表 2.3 個標準誤差以外,單邊表示的或然率量,雙邊的積分所得就是 0.0213730758 ,意即發生的可能性只有 2.13730758% ,或明確的說成要測量 46 次以上,才有可能出現一次。

Fig. 3 單邊 2.3 倍標準誤差以外的或然率示意圖


2025年10月16日 星期四

朗誦控制程式

朗誦控制程式


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


這是一篇以語音輸出為題,展示如何將一個很基本的語音輸出功能,發展成能讓系統僅只需操作 READ 指令,就能唸出整篇文章並進行控制之程式的設計方法。

1. 程式特性

這種應用範例,就如同電腦繪圖,看起來功能非凡,但通常都與電腦作業系統的基本性能高度相關,因此,發展出來的程式有時效性,而且只能配合硬體既有的能力來設計,程式移往另外一套電腦或系統,就不能使用。

此類程式也因此而不可能規劃出標準指令,最終發展出來的應用程式,都得因時、因地制宜,僅安排特別指令,或只有幾個簡單規則指令。

語音輸出本身,是一項很大的課題,我們不可能從最基本的硬體操控來設計程式,現行作業系統的設計觀念,提供了發展者可以叫用程式來使用的基本運用方法, Win32Forth 系統因此亦跟著建立起叫用作業系統現成程式的功能,前人詳細讀完作業系統叫用程式的說明資料後,才設計出相關的叫用程式,我們的程式才能夠跟著使用。

語音輸出的基本功能已經存在於 Win32Forth V6.14 版本的系統中。換句話說,語音輸出的功能要求,已經不勞我們傷神。但原始程式提供的功能,只能操作系統,讓它一句一句的講人話。我們的目標,則是憑此功能,設計出能夠一次就唸出整篇文章的程式來。

『原始功能』與『我們的目標』間的差異,從 FORTH 系統的傳統觀念來看,主要就是被唸的文字,應該放置在那裡?有所不同而已。因此,我們打算設計的程式,就得設計出文字處理功能。另外,它牽扯到檔案操作、字串處理、與一些簡單的語音輸出固定規則。

想設計出這個應用程式之前,必須先行了解已有程式的性能。由於沒有任可可以參考的書面文件,所有性能都要靠操作電腦、實測、記錄後,才能完成,要耗不少時間。詳細的測試過程,作者將其寫成一篇網文,張貼在作者的個人網頁上,供大家參考,是 2010-8-2 貼出的『因子分解法』。(補註 : 該文已從本網頁撤除)

該文只作經驗記錄,沒有刊出程式,本文才刊出實際可用程式,為了節省本書篇幅,僅節錄該文部份相關敘述於此,以便讓讀者明白,在程式中為何出現一些將待講文字進行特殊處理的修改設計?然後才交給電腦程式執行語音輸出。

『因子分解法』文內,是這樣寫的:

這半個月,正式開始了語音輸出的程式設計,經過長期的積極準備,心中早已有了如何進行這件工作的具體腹案,因此,進展順利,截至昨天為止,『唸文章』的功能幾乎全部實現了,唸的是英文,想唸出中文,以後再說。

我每天在住家附近散步時,都會經過一條名叫 Rothesay 的街道,我實在是不知道街名的正確發音為何?街的名稱只是一個名字,字典上查不到,因此,長期迷惑於這條街名的唸法。我們散步時,也曾經有外來訪客,拿著地址字條問過我們,這條街在那裡?我告訴他方向時,曾試著想聽對方如何唸這條街名,但經我一問,他反而猶豫不敢發音了,只笑一笑回答說:『第一次來,也不知道該如何唸』。換句話說,我以一個中國人的身份問問題,所有天生講英文的朋友,都可能失去回答此一問題的信心,英文名字確實是可以隨著不同地方的人而唸成不同發音的,我們現在居住的都市 Hamilton 的唸法,英式發音就與美式發音不同。台灣宜蘭五結有一個『利澤簡』,會講台語的同胞,也不見得能用台語直接講對這個地名,不信?您現在就試試看,然後打電話到利澤簡派出所或消防隊,驗證一下發音。

現在,我找到一個充滿信心、敢於直接回答,永遠都可以請教的對象了,它就是我設計出來的軟體。即時性控制(Real time control)是 FORTH 的專長,自從大系統不能直接執行P@、P! 來進行硬體實質控制之後,我們對 FORTH 的感覺,可以說是心都涼了半截,此次設計電腦語音輸出時,卻可以排除這種遺憾。目前我所辦到的功能是:執行 READ filename ,可以得到名為 filename 的文字檔案,並自動唸完整篇文章,執行期間始終呈現即時性響應的感覺。

作者戲稱這種程式設計的方法,是一種『吸功大法』,性質確實如此。因為實際的語音輸出功能,我們根本不需要設計,是別種 C 式程式語言,先行設計好的動態連結程式,幫了大忙。而 Win32Forth 系統,只設計了吸功大法所需要的指令,將別人辛苦練成的功夫,全面吸取來用,完成了語音輸出的所有功能,『因子分解法』中,是這樣寫的:

現代 FORTH 系統內的功能,還真像金庸的小說:『天龍八部』裡面,那位來自大理國的段譽,身懷北冥神功的絕技,具有化功大法,有武功的人一旦被他沾上,立刻吸來自用。您若精研 Win32Forth 裡面的特異功能,發現它可以呼叫 C 式語言設計出來之 .dll 程式庫內所有 API 功能程式時,可以了解它簡直就是這種吸功大法,此法還不可逆,暫時是別的程式語言難以辦到的,要不然別的程式語言,只好將整個 FORTH 系統全請去用。

我這次進行的語音輸出程式設計,正在全面使用此一吸功大法,是借力使力進行程式設計。練得這項吸功大法,確實耗費不少時間,我完整的練成過用 C 設計程式,然後由 FORTH 『吸用』的詳實步驟,確實像北冥神功,而且這種功夫練會一次就夠了,以後都是別人設計程式,我只需借力使力叫來使用,這種功夫好用得很。

關於利用 FORTH 直接測試語音輸出性能的部份,『因子分解法』中,是這樣寫的:

我在利用 SAPI(Sound Application Program Interface) 程式設計語音輸出時,首先要做的事情就是因子分解,『講人話,唸文章,配合視障操作而反應』就將整個打算設計的系統一分為三了,接下來繼續像樹狀分支一般的再將問題分解。講人話是別人的作品,我不用傷腦筋,我只需善用吸功大法,搞清楚吸來使用時該有的規矩就夠了。

即時性控制功能的妙用,就是能讓使用者即時的獲得執行結果,只要花一點點的時間,您就能體會這一套軟體講人話的效果到底如何了?憑良心講,如果不是利用 FORTH 具有直接操作,能立即得到結果的特性,我根本就不想設計這種系統,換成不用 FORTH 來進行這種發展,情況可以想像,絕對是笨透了。直接操作時,我將測試因子分成數個,先大略分成講中文與講英文,再細分成亂講、講單字、講連體字、講符號、與以上皆有的混講,還得把整個鍵盤全面試透,產生出來的效果完全不同,就算有說明書描述這些細節,都絕對不如我用手直接操作,用耳直接聽取來得乾脆,這豈不就是視障者的程式設計方式?有超出原本預料之處,立刻留下記錄,後續發展需要知道這些特性。

試完之後,發現光是講單字,配上少數幾個不同符號,音調的高低就可不同,因此,讓電腦講中文的五音就也可行了。我將希望測試出來的效果,再度因子分解成一份固定的表格,經過兩天的摸索,就把表格填妥了,這就是 FORTH 。我絕不相信現行世界上其他的任何程式語言,能這樣子完成測試, Python 不知道會不會講話? Java 呢? Matlab 呢?我的嬰兒期啟明 FORTH ,開機點兩點後就會講人話,還是不用花錢買的東西。

接著,我規劃出要系統講整句話的測試,先以直接鍵入(Type-in)的方式來測,隨後就又發現,句中、句尾使用不同標準符號時,又出現了不同效果,必須繼續因子分解,列出所有可能的符號與句子組合,然後,操作 FORTH 唸出句子,發現電腦裡面的這個美國人確實聰明,唸得真像英文,該問則問,該嘆就嘆,該輕必輕,但中文講得不太好,有點生硬,還需要更多時間來揣摩。

為了集中精力完成『唸文章』的要求,我停止了講中文的測試與發展,以免干擾接下來更為複雜一些的程式設計。我再度將發展項目因子分解,先將唸文章的方式分解成兩個,一是全面翻譯本文成可唸文,然後直接唸全篇文章,另一方式是逐句翻譯,逐句講。前者的程式較容易設計,可是系統耗用大量的記憶體,講出之話,段落不太分明,是其缺點。後者的程式要考慮之處較多,解決所遭遇的問題時,必須自己動腦筋想辦法,很有挑戰性,但系統耗用記憶體較少,將來與別的功能程式,存在同一 FORTH 系統中時,影響較小,而且,最重要的是,要電腦以這種方式講話時,才比較像真人在講話,段落分明,正確的設計應該要採用後者。另外,測試程式時,發現了無法追蹤下去的不合理問題,如果每次講完話後,不強行將講話區域的記憶體內容清理乾淨,前面留下來的東西,會自動縮黏到新話語的尾部,繼續發音講話,沒有 FORTH 的性能,想得知此事,難啊!

程式設計完成到這個階段時,前述的兩個指令就已經設計出來了,基本上,在 FORTH 系統內自行打轉,都沒有問題,我指的是:文章用 FORTH 系統的編輯程式自己準備,然後全面唸出來,變得輕而易舉。我休息了幾天,建了幾篇『伊索寓言』(The Fables of Aesop)中的短文來欣賞,剛開始聽講時不太習慣,必須調整講話速度,系統以設定負數值來調慢速度,我需要用 -3 的設定才能完全聽得清楚,聽過幾篇後,就完全能用正常速度聽講了。

我自學英文的過程中,一直強迫自己聽英文時不要看文字,才能加速適應不同人講的英文,對這套系統而言,情況相同。我為視障朋友發展系統,系統設計出來後,我當然必須假想自己就是一名視障者,根本無書可看,完全要用聽力,要把狀況想像成,我現在比視障者更可憐,連點字都摸不出來時該怎麼辦?讀者您讀到這裡時,您實在該慶幸自己還有這麼健全的視力來看我的文章,但也不要忽略我們還有許多視障同胞,需要我們的幫助。

唸文章的軟體工程到此並未結束,我想唸的文章,不能只是使用 FORTH 系統自行建立的檔案文章才能唸,必須接受所有可能呈現在這個系統裡面的各種檔案格式才行。這項要求是件新的挑戰,一般程式語言更難處理,因為不同編輯軟體,編寫出來的純文字檔案全都不同,我又得進行因子分解了。

平日,我除了搞 FORTH 與打網文外,只上網看新聞與搜索資料,我不是聖人,偶爾欣賞一下清涼的美女照片也是難免的,經常抓資料儲存的方式,都用微軟的編輯軟體 Word ,現在正在打網文,用的也是 Word 系統,至於其他的編輯軟體,沒有使用經驗,也不想勉強去接觸。因此,這次的因子分解,就將問題分解成四個,一是用 FORTH 編輯程式,就是那片綠葉子 WinEd 建的文字檔,二是用記事本建的文字檔,三是用文件檔案編輯程式(WordPad)建的文字檔,四是用文書編輯軟體(Word)建的文字檔。

以前,讀別人寫的英文電腦書籍時,讀到過強調這些編輯程式編出的文字檔案,內容會有所不同的特別聲明,自己卻不曾仔細的看個明白,到底其中有甚麼不同?現在,為了程式設計需要,不得不碰了,趁機讀個仔細,然後從中尋找規則,最後才能設計出適用於各種編輯軟體,所編寫出來之文字檔案的程式。

能看透別人的規格,是件痛快的事情, FORTH 才能輕而易舉的辦到,因為系統中早就有現成的指令可用,我已用了 30 年,但現在才拿來看所有的文字檔,看完了立刻發展程式解決問題,也把原來發展成功的唸文章程式,改寫成更具有彈性的設計,可以輕鬆插入新面臨的不同格式文字檔閱讀要求之指令,如此一來,程式就可以適用於所有可能出現的狀況了。

讀過了四種不同的文字檔結構內容,還都分別為它們設計完適應性的程式,現在已經不怕再有新規格文字檔案出現了,因為頭腦已經被磨練過幾次,知道如何適應新問題,換句話說,將來應該沒有我的啟明 FORTH 系統讀不成的文字檔案,這些文字檔案暫時還須要根據圖示後人工識別檔案的格式,將來,我希望能做到全由程式自己識別,這一部份的發展就可以全部告一段落了。

比較上述四種文字檔案的規格後,發現由 WordPad 建成的文字檔比由 Word 建成的文字檔還難處理,遭遇困難時,我仍是採用因子分解法,將問題因子化成一個一個的問題,然後逐個解決,這半個月,我不停的採用這種解決問題的方式進行程式設計,效果非凡,程式設計得非常順利。

我自己也急於想享受設計出來的成果,好像已經有許久沒見到純用 FORTH 控制東西的成果了,現在又重拾這種愉快,還是講人話的東西,我很喜歡。我知道這樣的設計,可以幫助台灣健康的同胞學習英文。

仔細追究這套方法,能用到的語音輸出指令,總共只有三個,首要的第一個,是一個前引指令 VOICE ,它的用法,如同必須與後續指令形成片語,後續有限的幾個語音輸出控制指令,才得以被執行。第二個是設定講話速度的指令,名為 SETRATE ,執行前賦予數值,就能改變講話速度,給負值就變慢,給正值就變快,執行讓速度變慢的操作範例如下:

-3 VOICE SETRATE

我們打算設計的程式,不需要動用到設定講話速度的 SETRATE 指令。讀者只須知其功能,而在需要調整講話速度時,直接操作使用。

結果,最後僅剩第三個唯一的指令,是我們想要設計程式時,可以使用的指令了。指令的名稱是 SPEAK ,在原始源程式檔案 EX_SAPI.F 中,所記錄的範例用法為:

0 0 u" Hello World!" drop voice speak

一個 FORTH 程式老手,可以僅憑對上述指令執行時,指令間參數傳遞狀況的分析,了解自己可以如何運用這些指令。上列程式對大家可能造成的困擾,來自於由原設計人狄湯瑪(Thomas Dixon)自行添加的 u” ………” 指令,在系統中可以透過直接操作 SEE u” 來看清它的詳細設計內容,它還是一個立即執行性的指令。

事實上,經過仔細追究後,想利用 SPEAK 指令的方式,沒有那麼複雜, SPEAK 指令僅只需要先行提供一個安放字串處的起始記憶體位址就夠了。原作者純粹只是為了讓 SPEAK 能夠直接接受就地處理出來的字串,才會那樣設計 u” 指令。

分析結果告訴我們,想要操控 SPEAK 指令,下命令的方式可以簡化成:

0 0 address voice speak

其中的 address 代表待講之話的字串所安放的起始位址,而上述指令執行完畢之後,堆疊上會留下一個代表執行順利成功或失敗的旗號值。

僅僅就是這麼一丁點的分析,就足夠讓我們進行自己打算設計之程式的規劃了。想讓系統自動的唸出整篇檔案文章,就得設計檔案讀取程式。讀到的文章要放在那裡?得自行規劃。

檔案內存在了會妨礙 SPEAK 指令執行或影響效果的符號,得設計程式先行將其處理掉。考慮到人機界面問題,試成程式之後,就改良內容,讓它顯得友善。這些要求,全由純粹傳統的 FORTH 程式設計方式來完成,非常簡單,因為, FORTH 就擅長於搞自動控制,而上述這些要求,就是最典型的自動控制問題,只不過控制對象是字串與語音輸出而已。

2. 唸檔案文章的程式


 

\ Text to speach 2010-7-23 by Ching-Tang Tseng

needs FCOM.F

\ SAPI 5.0 typelibrary
5 0 typelib {C866CA3A-32F7-11D2-9602-00C04F8EE628}

ISpVoice ComIFace voice

\ Create voice instance
voice ISpVoice 1 0 SpVoice CoCreateInstance drop

0 VALUE FILEID            \ file identification
0 VALUE FPT1              \ file pointer 1
0 VALUE SPT               \ scanning pointer
0 VALUE CPT               \ coding pointer
0 VALUE BPT               \ beginning pointer
0 VALUE EPT               \ endding pointer

: FBUF1  ( -- n )         \ file buffer 1
  HERE 2000 + ;

: READING-BUF    ( -- n )          \ reading buffer
  FBUF1 FPT1 + 10 + ;              \ keep 10 bytes away from file buffer

: ASSIGN-FILE-NAME    ( -- adr slen )
  BL WORD COUNT ;

\ using example: GET-FILE FISHERMAN.TXT

: GET-FILE
  FBUF1 4096 0 FILL 
  ASSIGN-FILE-NAME  R/W      OPEN-FILE
  IF CR ABORT" OPEN-FILE error?"  THEN 
  >R  R@ TO FILEID 
  CR ." fileid is: " R@ .
          FBUF1 4096 R@      READ-FILE
  IF CR ABORT" READ-FILE error?"  THEN
     DUP TO FPT1
     CR . ." Bytes is reading!"
                     R>     CLOSE-FILE
  IF CR ABORT" CLOSE-FILE error?" THEN
;

: CODING  ( n -- )
    CPT C! 1 +TO CPT
  0 CPT C! 1 +TO CPT ;

: CHANGE-CODE    ( n -- n )                  \ improve the voice
  DUP
  CASE
    ASCII ( OF  DROP BL ENDOF                \ ( --> BL
    ASCII ) OF  DROP BL ENDOF                \ ) --> BL
    ASCII . OF  DROP ASCII , CODING 0 ENDOF  \ . --> , 0
  ENDCASE
;

: RANGING ( -- )
  FBUF1 TO BPT
\ for Word text file:     FBUF1 2560 + TO BPT
\
\ may be used here by other kinds of text file.
\
  FBUF1 FPT1 + TO EPT
  BEGIN
       EPT C@
       32 <
  WHILE
       -1 +TO EPT
  REPEAT
;

: PRINT-TEXT-FILE  ( -- )
  CR CR
  BPT DUP EPT SWAP - TYPE ;

: TEXT-TO-SPEACH  ( -- n )
  0 0 READING-BUF VOICE SPEAK ;

: SPEAK-A-SENTENCE  ( -- )
  READING-BUF TO CPT
          BPT TO SPT
       BEGIN
           SPT C@  1 +TO SPT
           DUP DUP 32 <
           IF
              DROP
           ELSE
              CHANGE-CODE
              CODING
           THEN
           ASCII . =
           IF
               TEXT-TO-SPEACH
               IF   QUIT
               ELSE READING-BUF 1000 0 FILL
               THEN
               READING-BUF TO CPT
           THEN
        SPT EPT >=
        UNTIL
;

: READ  ( -- )
  GET-FILE
  RANGING
  PRINT-TEXT-FILE
  1000 MS
  SPEAK-A-SENTENCE
  TEXT-TO-SPEACH
  DROP ;

\ -1 VOICE SETRATE DROP
\ READ CCT.F
\ READ FISHERMAN.TXT

: SPEAKING  DUP >R FBUF1 SWAP MOVE R> TO FPT1
 RANGING PRINT-TEXT-FILE SPEAK-A-SENTENCE TEXT-TO-SPEACH DROP ;

\ : TEST s" I am speaking. " SPEAKING ;

: SAY 0 0 [COMPILE] U" DROP VOICE SPEAK DROP ; IMMEDIATE

\ SAY This is a test.



3. 程式說明

程式內指令使用的名稱,事實上已經包含了簡要功能的說明意義,可用的 READ、SPEAKING、SAY 新增設計指令的後面,也提供了最簡單的使用範例,作者並不希望太過詳細解釋指令的內容,規劃程式的觀念則值得詳細說明。

簡單說明指令功能如下: READ 可用來直接讀出一篇檔案文章。 SPEAKING 可用於程式執行後進行語音輸出。 SAY 指令則可用來直接試驗語音輸出。另有一個可以很方便的讀取任何檔案,以便觀察檔案內容的萬用指令 GET-FILE ,發展其它程式時,可以用到,這個程式首先就得靠它完成檔案內容的分析工作。

在 FORTH 的學習過程中,這個程式所使用到的指令,因涉及字串處理,大約只能算是第二級的困難度而已,所以也不難看懂。

我們想要系統自動讀出來的文章,是放在一個檔案之內,因此,程式的起始處,就先行規劃出兩個記憶體緩衝區,第一個用來放置從檔案讀進來的整篇文章,第二個用來放置每次從文章中抽取出來的一段完整句子。為什麼要這樣規劃?在我的網文『因子分解法』中,詳細敘述了係經過仔細研究後,得到的結論。

在 FORTH 系統中,使用者每逢必須規劃自己的緩衝區問題時,設計上的習慣,都是就從系統字典浮動的尾端,往上選一個區域來規劃,這樣,緩衝區就可以有一個彈性的起始位址。 PAD 為什麼被用來作為字串處理緩衝區的起始位址,道理也在這裡,它是一個可以隨著系統增長而水漲船高、自由浮動、見官就大三級似的緩衝區規劃方法。

這個程式中讀進檔案的緩衝區,在字典上方加上 2K 的記憶體量,比 PAD 高很多,可以確保系統運轉時,不會干涉到字串處理程式可能必須用到的 PAD 緩衝區。由於可以同時開啟的檔案不必僅限一個,所以此區的起始位址就名為 FBUF1 。

待讀語句所需要的緩衝區,就規劃在讀進檔案緩衝區的上方,名為 READING-BUF 。檔案被讀進系統後,長度是固定的,因此,這個緩衝區的起始位址,使用時雖然也是浮動的,但在檔案被成功讀入之後,就會自動被確定下來。

緩衝區內的資料,要被逐次的處理,處理時就需要夠用的指標來指到處理位置。根據設計上的需要,程式中規劃了五個指標:

0 VALUE FPT1 \ file pointer 1
0 VALUE SPT \ scanning pointer
0 VALUE CPT \ coding pointer
0 VALUE BPT \ beginning pointer
0 VALUE EPT \ endding pointer

一號檔案指標 FBUF1 固定的表示一號檔案讀進系統的長度,掃瞄指標 SPT 則在程式執行過程中,會在讀入檔案緩衝區內游走,表示資料被處理到了那一點?編碼指標 CPT 則在待講語句緩衝區內游走,代表待講語句已經被處理到了那一點?起點指標 BPT 與終點指標 EPT ,指到讀入檔案的特別起迄點,因為某些文字編輯系統所編寫出來的檔案,前後都夾雜了相關於文件的格式碼,這兩個指標用來去除不該被唸出來的資料。

所規劃的緩衝區與需要用到的指標,可以使用一張明確的記憶圖譜來表示,使用者就更容易明白設計這種程式的方法,記憶圖譜如下圖所示:

規劃緩衝區及指標之記憶圖譜

上述說明,就足以說明程式的規劃內容了。接下來,我們為這個程式設計了檔案讀取的完整程式,它可以作為所有程式的讀取之用,包括非文字式的檔案,所以也是個萬用程式。這個程式自身的發展過程中,也用這個 GET-FILE 指令的功能,分析確認各種文字檔案的內容,以便繼續設計程式。

在固定的作業系統中,檔案傳輸協定是固定的規定,本文不詳細解釋。

在分析文字檔案內容的過程中,作者發現某些文字資料區域內的符號,也會影響文字的語音輸出,因此,設計了 CODING 與 CHANGE-CODE 兩個指令,來換掉那些符號,整篇文章的讀出效果,就能比較令人滿意。

RANGING 指令,是用來去除讀入檔案前後無關之格式碼時使用的,為適用於各種不同的文件編輯系統,這個指令可以隨狀況而進行千變萬化的修正設計,任何一家商售文件編輯系統處理出來的文件檔案,都可以透過這個指令來進行待講出之前的預處理。由於它要處理的對象,種類可能非常多,此處不詳列適應於各種情況的設計內容,僅表徵性的列出兩種,一是針對 FORTH 系統自身可以編寫產生的 .F 式文字檔,另一種是一般編輯系統如 NotePad 能編寫產生的 .TXT 式文字檔,程式說明中,附贈一個可以處理掉 WORD 文件起頭格式碼的說明敘述。

剩下的四個指令:

PRINT-TEXT-FILE、TEXT-TO-SPEECH、SPEAK-A-SENTENCE、READ

功能就都各如其名,不必解釋。

依例,我們載入程式後,執行 READ 指令,唸一篇範例短文,螢幕上會先行印出整篇文章,等待一秒過後,自動讀完。效果顯示如下:


ok
READ FISHERMAN.TXT 
fileid is: 288 
666 Bytes is reading!

The Fisherman.
A fisherman had been fishing all day without catching anything.
As evening fell he found a very small fish on the end of his line.
"Please spare me," squeaked the little fish. 
"Please I am too small to make you a good meal for I am not fully grown yet, Why don't you throw me back into the river. 
Then, when I am bigger and worth eating you can come back and catch me again."
"No, no," said the man, who was very hungry. 
"I am going to hold on to you now that I have caught you.
If you once get back into the water you will take very good care not to come near me again and I shall have no supper now or later."
Hold on to what you have ok


我們也可以透過操作 GET-FILE 指令,來仔細分析別種編輯系統編寫儲存的文件檔案內容。下列為 WordPad 編寫出來的範例文件檔案內容,只要知道這樣的內容,我們就能相應的設計程式,修改出可以交給 SPEAK 指令進行語音輸出的文章。以下是作者臨時利用 WordPad ,隨便編寫一些文字,儲存成檔案後,直接操作這個程式,再執行系統中現成的傾印(DUMP)指令,在螢幕上顯示出來的檔案內容。


 
GET-FILE wordpad.txt 
fileid is: 288 
468 Bytes is reading! ok
fbuf1 500 dump 
  44A948 | 7B 5C 72 74 66 31 5C 61  6E 73 69 5C 61 6E 73 69 |{\rtf1\ansi\ansi|
  44A958 | 63 70 67 39 35 30 5C 64  65 66 66 30 5C 64 65 66 |cpg950\deff0\def|
  44A968 | 6C 61 6E 67 31 30 33 33  5C 64 65 66 6C 61 6E 67 |lang1033\deflang|
  44A978 | 66 65 31 30 32 38 7B 5C  66 6F 6E 74 74 62 6C 7B |fe1028{\fonttbl{|
  44A988 | 5C 66 30 5C 66 6D 6F 64  65 72 6E 5C 66 70 72 71 |\f0\fmodern\fprq|
  44A998 | 36 5C 66 63 68 61 72 73  65 74 31 33 36 20 5C 27 |6\fcharset136 \'|
  44A9A8 | 62 37 5C 27 37 33 5C 27  62 32 5C 27 64 33 5C 27 |b7\'73\'b2\'d3\'|
  44A9B8 | 61 39 5C 27 66 61 5C 27  63 35 5C 27 65 39 3B 7D |a9\'fa\'c5\'e9;}|
  44A9C8 | 7D 0D 0A 7B 5C 2A 5C 67  65 6E 65 72 61 74 6F 72 |}..{\*\generator|
  44A9D8 | 20 4D 73 66 74 65 64 69  74 20 35 2E 34 31 2E 31 | Msftedit 5.41.1|
  44A9E8 | 35 2E 31 35 30 37 3B 7D  5C 76 69 65 77 6B 69 6E |5.1507;}\viewkin|
  44A9F8 | 64 34 5C 75 63 31 5C 70  61 72 64 5C 6C 61 6E 67 |d4\uc1\pard\lang|
  44AA08 | 31 30 32 38 5C 66 30 5C  66 73 32 30 20 54 68 69 |1028\f0\fs20 Thi|
  44AA18 | 73 20 61 20 74 65 73 74  20 66 69 6C 65 20 66 6F |s a test file fo|
  44AA28 | 72 20 74 65 78 74 20 74  6F 20 76 6F 69 63 65 20 |r text to voice |
  44AA38 | 73 70 65 61 63 68 20 74  65 73 74 69 6E 67 2E 5C |speach testing.\|
  44AA48 | 70 61 72 0D 0A 54 68 69  73 20 66 69 6C 65 20 69 |par..This file i|
  44AA58 | 73 20 70 72 65 70 61 72  65 64 20 62 79 20 57 6F |s prepared by Wo|
  44AA68 | 72 64 50 61 64 20 73 79  73 74 65 6D 20 61 6E 64 |rdPad system and|
  44AA78 | 20 68 61 73 20 62 65 65  6E 20 73 61 76 65 64 20 | has been saved |
  44AA88 | 69 6E 74 6F 20 74 68 65  20 73 61 6D 65 20 64 69 |into the same di|
  44AA98 | 72 65 63 74 6F 72 79 20  77 69 74 68 20 46 6F 72 |rectory with For|
  44AAA8 | 74 68 20 73 79 73 74 65  6D 2E 5C 70 61 72 0D 0A |th system.\par..|
  44AAB8 | 49 20 61 6D 20 67 6F 69  6E 67 20 74 6F 20 74 65 |I am going to te|
  44AAC8 | 73 74 20 69 73 20 74 68  65 72 65 20 61 6E 79 20 |st is there any |
  44AAD8 | 70 72 6F 62 6C 65 6D 20  69 74 20 77 69 6C 6C 20 |problem it will |
  44AAE8 | 68 61 70 70 65 6E 20 77  68 65 6E 20 66 69 6C 65 |happen when file|
  44AAF8 | 20 69 73 20 72 65 61 64  69 6E 67 20 62 79 20 74 | is reading by t|
  44AB08 | 68 65 20 73 79 73 74 65  6D 2E 5C 70 61 72 0D 0A |he system.\par..|
  44AB18 | 7D 0D 0A 20 00 00 00 00  00 00 00 00 00 00 00 00 |}.. ............|
  44AB28 | 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00 |................|
  44AB38 | 00 00 00 00                                      |....| ok


其它格式的檔案,可以依樣獲得結果,為節省篇幅,不再舉例。

4. 討論

自動唸出檔案文章的程式,不涉及數學計算,所以,如果還堅持硬要使用 ABC FORTH 系統中的 BASIC 格式,來完成程式設計,就會是一件毫無意義的事情。使用純粹的 FORTH 傳統設計方式。

一個在學的學生,數學與英文的學習課業,佔有很重的份量,而且也是比較難學的課業,好的學習工具則能協助學生,解決一些學習上可能遭遇到的問題。程式語言就是一種工具,這本書舉用的數學範例程式,足以顯現系統能夠協助學生,解決數學問題的能力了。而這篇文章,則能顯現系統能夠協助學生解決學習英文時『說』方面的問題。也許語音輸出的品質仍有強烈的機械式音感,但是,這只是一個初次實現目標的範例程式,向這個方向踏出去的第一步,意義深遠。此前的流行程式語言或套裝軟體,罕見具有全面性混合功能的性能, FORTH 在各方面的發展,一向都是輕而易舉的就能辦到,電腦開機後,啟動 FORTH 系統,所有功能就已經存在其間的程式語言,目前幾乎是找不到的東西。

當使用者啟動 FORTH 系統之後,使用者就可以在程式中自由使用指令來設計會講話的程式,現在,作者設計的 ABC FORTH 系統已經辦到了。搞 C 式程式語言的人,您的系統隨便的就能提供這些性能嗎? Python 行嗎?賣得那麼貴的 MatLab、Mathematica 數學套裝軟體行嗎?都不行,對不對?大家好好的向 FORTH 學習吧,趕快跟進,本書作者輕而易舉的就辦到了。

作者長期僑居於紐西蘭,曾在大學裡見過海外來的法國學生,在需要參加研究成果發表會前,先行寫好一長篇英文講稿,編寫成檔案儲存起來,然後透過專門的語音輸出系統,唸出整個檔案內的講稿文章,以便進行聽覺修正。那位學生使用的是功能專一性的軟體系統,現在,我們的 FORTH 系統,則可以提供同樣的功能,提供給全中國的同胞免費使用,正面意義重大!

能唸得出英文,不是甚麼大不了的事情,但是,讀者之中,可能沒有人像作者一樣,妥善的運用過這一套語音輸出的功能,深入研究過其它相關的問題。它能按照英文字母式的漢語拼音規則發音,或者其它國家的語言,所以也能發出品質不太優良的中文語音輸出,還能唸出字典上也無法標示的英文字唸法。

我們在執行複雜的程式,執行完畢後,決策的輸出,如果強調非用語音不可,這個程式已經顯示, FORTH 程式語言已經具有現成的功能了。

大學英語系的學生,或喜歡研究英文詩的學者,強調唸出英文詩的效果,要隨心所欲時,這個程式也辦得到。英文詩的唸法注重輕、重音,與中文詩或拉丁文詩的注重長、短音有所不同,這個程式也都辦得到。這一章的範例程式,只是精簡初步的應用範例,深入發展,還能提供多方面的應用功能,例如:就依本章程式,繼續發展,要求系統按指定規則,自動或重覆唸文章中特定句子的功能,也很容易辦到。

這些後續的應用舉例,不在本篇文章的設計範圍之內,我們並沒有設計相關的程式來展示。

這個範例程式發展的初衷,係起因於想為視障朋友免費貢獻一點心力,協助他們使用電腦,但還未曾實際的應用於任何場合,以便接受考驗後設計出最佳性能。不過,只要使用人提得出具體要求,對作者而言,應該都是很容易就能解決的問題。這是一件很有意義的工作,能夠為人,也可能為己,人人都會老,聽覺據說是最後才會消失的感覺,有此程式可用,能為老者唸文章。

2025年10月1日 星期三

游標控制

游標控制


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


在 Forth 系統中,由於控制游標是一個與硬體顯示視窗有關的問題,控制游標的指令不被列入標準指令中。

Forth 系統在印出訊息時,控制印出訊息之格式的能力是很強的,這在此前本網頁的眾多例題中可以看得出來。與印出訊息有關的指令是 EMIT, SPACE, SPACES, CR, TYPE..... 等,以及所有與印出數字有關的指令 . (dot), U. (U dot), D. (D dot), R. (R dot), D.R, U.R, # (sharp), <#, #s, #>..... 等等指令。

上述指令均使用於系統游標到達當時所處位置後的控制,不需要將游標跳往視窗中其他的任意位置。能辦到任意跳動之功能所需要的指令,傳統上將其名稱定名為 gotoxy ( x y -- ),意思很直接,不需要特別解釋。橫是列,直是行, x 是第幾列的數字,y 是第幾行的數字。

只要是跟硬體有關的操作指令,通常都不在初建 Forth 核心基本系統時被列入考慮,以簡化發展。因此,許多精簡的 Forth 系統都不具有 gotoxy 指令,我個人愛用的 32 位元 Win32Forth 系統具有,但轉入 64 位元後的 Lina64 Forth 系統沒有。

很幸運, Lina64 Forth 系統的作者,根據 Linux OS 的性能,提供了一個易於借用作業系統中現成功能軟體來完成某些特定工作的指令,名稱叫作 system ( sc -- )。它很有用,善用它,就知道如何來擴充系統各種的功能,想設計出 gotoxy, !xy, @xy 這一組指令時,就能靠 system 指令完成設計。

要如何設計? 寫一大堆說明,不如給大家實際範例。讀過本文中的下列程式,使用過簡單範例 main 後,大家就很容易了解這種程式技術,它在許多場合能被依樣畫葫蘆,例如:我在設計語音輸出、操控其他語言或系統或軟體、取放與硬體相關的數據.....等等等問題時都派上用場。

這一個簡例,可作為如何運用 system 指令的標準範例。實際上,我在想要觀察記憶體內容會高速變化的狀況時,它會非常有用,否則,只能採用一直橫列印出數據的方式高速滾動列印出結果,對高速動態變化內容的數據變化狀況很難觀察與體會,其感覺係寒天飲水,冷暖自知。這樣的使用實例,在2025年5月2日貼出的網文 『時基』一文中展示過。但該文內未提及此 gotoxy 指令組的來源,它係加裝 tput 公益應用軟體後獲得的可加裝功能,加裝 tput 軟體按下列操作便能獲得:

$ sudo apt-get install tput

至於有關 tput 使用時該如何給予指定參數與指定的文字命令? 這些規定不在此處的討論範圍,讀者必須從網上自行了解,以下設計內容是在熟讀資料後才進行設計。

我們在發展與同一作業系統中並存於世之公益軟體的應用時,必須學會這種本領。沒有同時並存於世的公益軟體可用時,想自行設計出同樣的功能,並不是一件很容易的事情。所以我們在獲得了這許多的好處時,應該感謝那些貢獻者,我無法一一點名致謝,只能在用到時宣告。

我剛檢查過,使用 Ubuntu 20.04 版本的作業系統時有 tput 軟體可自由加裝,在 ARM 環境的作業系統,均暫時尚無類似 tput 的軟體可供加裝。

在某些 Forth 系統中也許不採用 gotoxy 指令命名,且功能略異,但共同目的都是設法便於控制游標,例如一次性的把游標跳到往下數列、數行的位置,其指令名稱可能是 at-xy,他有別於此處討論的 gotoxy 性能,at-xy 所提供的性能,因為游標只能往下跳,設計時可以不涉及硬體控制,使用時,請自行注意。

另外,具有多工性能的 Forth 系統,就能根據游標可以前往任意指定位置再回到原本位置的特性,而設計出可在同一視窗內能同時顯示多項內容的畫面,這樣,執行 gotoxy 後的顯示效果,就可以是動態的,或者是可以瞬間就暫態變化的。這一部份,涉及多工應用,不在本文討論範圍之列。


 
\ gotoxy

: test
  s" tput sc " system
  begin 
  s" tput cup 0 40 " system
  getticks 0 20 D.R
  key? 
  until 
  s" tput rc " system 
;

: !xy  s" tput sc " system ;
: @xy  s" tput rc " system ;
: gotoxy ( x y -- )
  s" tput cup " $buf $!
  swap
  (.) $buf $+!
  32  $buf $c+
  (.) $buf $+!
    0 $buf $C+		\ must to be used at the end
  $buf $@ system ;

: main
  !xy
     begin
           0 40 gotoxy 
           getticks 
           0 20 D.R
     key?
     until
  @xy 
;


2025年9月15日 星期一

為eForth設計高階定義的DO...LOOP指令群

為eForth設計高階定義的DO...LOOP指令群


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


 

\ High level defined DO…LOOP words set for eForth
\ Author: Ching-Tang Tseng , New Zealand , 10 Dec. 2008
\ Reference: (1). FORTH DIMENSIONS, V. XIII No. 6 , p.34 , Rob Chapman, 1992
\ (2). MASTERING FORTH, Chapter 12, p.172 , Martin Tracy, Anita Anderson, 1989
\ (3). FORTH ENCYCLOPEDIA, Mitch Derick & Linda Baker, 1982 
\ (4). ALL ABOUT FORTH, Glen B. Haydon, 1982
\ (5). eForth Implementation Guide, Dr. C. H. Ting, 1990
: (DO)  ( limit, index - - )
 SWAP R> SWAP >R SWAP >R >R ;
: (LOOP)  ( - - )
 R> R> 1 + DUP R@ <
    IF >R DUP @ +
    ELSE R> DDROP CELL +
    THEN >R ;
: (+LOOP)  ( n - - )
 R> SWAP DUP R> + SWAP 0< OVER
 R@ < XOR
    IF >R DUP @ +
    ELSE R> DDROP CELL +
    THEN >R ;
: DO COMPILE (DO) HERE ; IMMEDIATE
: LOOP COMPILE (LOOP) HERE - , ; IMMEDIATE
: +LOOP COMPILE (+LOOP) HERE - , ; IMMEDIATE
: LEAVE R> DROP R> DROP R> DROP ;
: I=L? R> R> R> DDUP >R >R = SWAP >R ;
: ?DO [COMPILE] DO COMPILE I=L?
     [COMPILE] IF COMPILE LEAVE
     [COMPILE] THEN ; IMMEDIATE
: J R> R> R> R> DUP PAD ! >R >R >R >R PAD @ ;
: K R> R> R> R> R> R> DUP PAD ! >R >R >R >R >R >R PAD @ ;



為了方便讀者節錄程式,與本文切要相關的資料領文先行,引用時請尊重原始出處。

1. 緣起

採用eForth32發展地震早期警報系統期間,希望轉殖Tiny BASIC Compiler程式於此系統時,遇到下列設計:

… ?DO … IF…LEAVE THEN …+LOOP…

因eForth缺DO … LOOP指令群,致使希望直接引用程式的目的無法立即實現,甚覺遺憾。若欲修改程式,則因結構煩瑣,費時費事,因而暫時中止了發展,只希望有機會能設計出此指令群,以便日後又有其他程式想直接套用時,更為方便。
此事顯示在發展新系統的初期,發展者最好考慮歷史的包袱,不要將任何傳統習慣隨便棄之不顧,否則日後有好的現成程式可資利用時反成困擾。當然,eForth原作者繆比爾(Bill Muench)已經表明他個人喜好FOR…NEXT勝於DO…LOOP。只用FOR…NEXT也一定可以設計出任何你想要的有限迴路程式來,本人也數度如此實際運用於須要的程式中,證明所言不假。
我尊重Bill Muench的個人見解,但我也尊重任何可貴的前人設計,二者皆不可棄。欲解決因此而導致不能相容的問題,最好的辦法就是自己動手建立起兩者間的友誼橋樑,給自己也給世人帶來永久的便利,我參考了前人已經提供的資料與技術,整理出此指令群與世人分享。
所要設計的指令群,還強調必須用FORTH高階定義的方式完成設計,才符合eForth精神。此前,我們已經有無數的FORTH系統可供參考來設計此指令群,但全都是以低階方式完成的設計,只能參考,不能照抄。eForth精神的可貴處,就是在其發展初期,係以簡化整體結構為主,只有少數限量的基本指令要用低階方式設計,執行效率不是強調重點,系統中大部份指令則都用高階定義方式完成設計。因此,若欲將DO…LOOP指令群也加入eForth的範本系統,卻又不破壞其原始精神,就必須是高階定義方式完成的設計。
以eForth為起點發展出FORTH所有功能的觀念一直強烈的深植我心,我希望能以實務進行驗證。我已見到了許多人在各種CPU上的努力成果,這是橫向的實例,表示未來出現任何新的CPU硬體,FORTH都可在第一時間,以最迅速的手法立即跟隨實現。然而,向上繼續加強的垂直發展則仍有廣泛空間,很須要好手透過實際使用經驗來累積成果,達到所謂『不做不知,一做便知』的進展。我在這次刻意挑選典型eForth32系統來發展應用程式期間,發現了這個問題,覺得可能會是未來eForth被引用時的障礙,只要力有所逮,不必推卸責任,應該促其實現,因而花了一點心思,設計出可以久放的程式。

2. 程式設計經驗

完成這個指令群設計的經過是一次很好的經驗累積,其中有許多寶貴知識是從FORTH專業書本中學習得來的,也有自己經過思考後,新獲得了對FORTH的深刻體認。一般而言,很少有人會為文介紹FORTH的核心指令是如何設計出來的?就像學術論文的發表一樣,常略去一些寶貴的研究細節。我們看FORTH系統的源程式時,看到的是創作人的最終成果,看不到創作人研發期間的心路歷程。如果學習FORTH或用FORTH發展程式,能同時學到一些傑出的設計方法,必然是很有助益,本文留下一些此方面的記錄資料。

3. DO、LOOP、+LOOP

在廣讀群書後,找到一份最接近設計要求可供參考的文獻是來自FORTH DIMENSIONS 1992年3月出版的V. XII No. 6期刊,作者:Rob Chapman在期刊的讀者來書欄中提供了一些高階定義方式完成的FORTH指令設計,上列(DO)、(LOOP)、(+LOOP)三個核心指令便來自該文,純高階方式完成的漂亮定義,引用時僅將ENDIF改成對應的THEN,CELL在eFroth32中使用4,其他均直接引用。相關於此指令群的其他指令設計則未在文中出現,因此,必須另行設法。
由於此指令群除?DO指令外均屬於F83標準指令,在『FORTH Encyclopedia』及『All About FORTH』兩本書中可以找到指令的詳細說明與執行細節,整理出DO、LOOP、+LOOP三指令的相關設計,以倒敘法列出如下:

: DO COMPILE (DO) HERE 3 ; IMMEDIATE
: LOOP 3 ?PAIRS COMPILE (LOOP) BACK ; IMMEDIATE
: +LOOP 3 ?PAIRS COMPILE (+LOOP) BACK ; IMMEDIATE
: BACK HERE - , ;
: ?PAIRS ( value1, value2 - - ) – 13 ?ERROR ;
: ?ERROR ( flag, error message number - - )
SWAP IF ERROR ELSE DROP THEN ;

更進一步的ERROR指令追蹤不擬繼續了,因為早期系統的完整設計,多加了在編譯過程出差錯時要提示使用者訊息的功能,我們的eForth力求精簡,指令設計的內容能省則省,但仍要不失其基本功能,套用的結果就得到了本文中這三個指令的最精簡設計。

4. LEAVE

不幸,另一個同為標準指令的LEAVE,書中全以低階定義的方式進行解說,一時也看不出其關鍵意義,最後放棄了參考書本中的說明,改採自行思考尋求解決方法。
我記得fig與83標準的差異中,LEAVE性能不同屬其中之一,fig於LEAVE被執行時,在DO…LOOP中,由LEAVE到LOOP之間的剩餘程式仍然會被執行最後一次,而83標準則由LEAVE直接跳至LOOP之外。
根據這個靈感,產生了純用高階定義方式設計出LEAVE的方法,可以強迫留在回返堆疊內的兩個DO…LOOP有限迴路指標之值相等,系統便會在檢查指標值時因相等而跳出迴路,問題是這樣的LEAVE規格就等同於fig標準,自LEAVE至LOOP之間的程式仍然會被執行最後一次,它的設計方式如下:

: LEAVE R> R> DROP R> DUP >R >R >R ;

測試的結果也差強人意,勉強有了一個fig標準式的LEAVE指令可用了,但仍心存遺憾,還想從書本中找出更好的設計,使之符合83標準,可惜身邊資源並不豐富,網上FORTH DIMENSIONS資料要逐期瀏覽又非常辛苦,因此又回到自行思考的模式來解決問題。
有一天心血來潮,想到了如果能用強迫迴路指標值相等的方法來終止迴路,那麼,系統隨後豈不也得將回返堆疊上的兩個無用指標值丟棄以終止迴路?我何不更進一步,讓LEAVE主動進行丟棄的工作?在執行LEAVE時,迴路的兩個指標值放在回返堆疊的第二及第三個單元內,第一個單元內放的則是指到LEAVE後面那一個指令的位址,若要執行LEAVE離開迴路,此執行位址就可以不要了,因此,可以確定LEAVE被執行時應該一次要丟掉回返堆疊上最頂部的三個值,至於第四個單元內放的是那一個執行位址?當時我也毫無概念。
設計程式測試就會得知結果,因此,再將LEAVE指令重新設計如下:

: LEAVE R> DROP R> DROP R> DROP ;

測試結果令我驚訝!達到目的了!當我再度閱讀范瑞克(Rick VanNorman)在Os2中設計的eForth系統源程式時,見到了低階設計的LEAVE指令內容,它就是以組合語言直接執行將回返堆疊指標減去三個單位量的方式完成的,現在了解它的真正意義了,也知道回返堆疊指標用的是CPU內的暫存器,高階指令無法直接更改其內容,只好連續棄除三個值來達到目的,上列意外完成的設計是對的。

5. ?DO

這個指令不屬於標準指令,因此在上述基本參考書中找不到相關資料。它也不是一個非有不可的指令,一個?DO…LOOP形成的結構,可以用下列程式取代:

2DUP = IF 2DROP ELSE DO … LOOP THEN

亦即,前面一序列程式可以取代?DO,但最後面兩個黏在一起的指令也得同時被使用,才能取代整個?DO…LOOP,若所配合的成對指令是+LOOP時,情況相同。以人工方式調整出對應的程式,不是此處要強調的重點,設計出?DO才是主要目的。我也不希望因為要增加一個?DO,卻必須回頭去修改已經完成了的LOOP及+LOOP指令。如此一來,就得自行思考解決辦法了。
崔馬丁(Martin Tracy)是一位設計FORTH系統的個中好手,台灣Forth同好都不應忘記當年我們採用他設計的MicroMotion Forth-79系統當推廣標準時,只買了一份他的創作,他的系統非常好用,完全透通,他算是讓FORTH在台灣推廣起來的偉大貢獻者,我們應當經常心存感激。他也有不少有關FORTH的著作,在他的『MASTERING FORTH』這本書中,我找到了有關?DO指令的設計方法,第十二章介紹Compiling words時解釋得非常詳盡,MacFORTH系統也已經採用這個方法讓?DO指令存在系統之中。但是,研究他介紹的設計後,就可以發現,前面已經設計好的DO、LOOP、+LOOP的執行內容都得跟著再增加變化方能配合,非我所願。
拜讀Martin Tracy的大作,給自己增加了設計FORTH程式時的另外一個新的技術經驗,他特別指出:集結一序列指令來替代另一指令的技術,稱之為巨集指令擴充法。( The technique of substituting one sequence for another is called Macro expansion. ),也舉了一個簡單例子,介紹在FORTH環境中要如何運用這種技術?例子是:
: 2DUP OVER OVER ;
: 2DUP COMPILE OVER COMPILE OVER ; IMMEDIATE

這兩種設計完成的指令,都可以達到相同的功能,但在後續的使用上,後者被限制成要用[COMPILE]指令來配合編譯。可是當系統隨後執行到2DUP時,兩種設計就會呈現絕對不同的執行方式。僅對這兩種設計進行比對時,前者多了兩個額外負擔的動作,首先要將執行位址碼從FORTH系統IP擠入回返堆疊,然後才執行兩次OVER,末了還要多執行另一個動作,必須將原擠入回返堆疊的執行位址碼取回,置入FORTH系統IP。而後者就直接執行兩次OVER,完成2DUP所要求的功能。後面這個設計顯然執行速度較快,它就是一種所謂的巨集指令擴充法的技術,與一般FORTH ASSEMBLER中經常用到巨集指令的功能完全相同,習慣上又被稱之為使用了指令別名( Aliasing )。
這是一種很可貴的技術,也讓我能實現?DO的設計。因為我已解決了LEAVE指令的問題在先,利用已存在的LEAVE,以及Martin Tracy所傳授的巨集指令擴充法,我便可以不用更改其他DO…LOOP指令群內已有指令的內容結構,亦能完成?DO指令的設計。
如果再添加一個檢查迴路二指標數是否相等的指令如下:

: I=L? R> R> R> DDUP >R >R = SWAP >R ;

那麼?DO的實際意義就可以等同於下列程式的意義,但不能就這樣直接使用:

DO I=L? IF LEAVE THEN

巨集指令擴充法設計出來的指令,都必須是立即執行式的指令,因此,?DO最後被設計如下:

: ?DO [COMPILE] DO COMPILE I=L?
[COMPILE] IF COMPILE LEAVE
[COMPILE] THEN ; IMMEDIATE

有限迴路的使用,除了使程式能固定的執行幾次迴路外,迴路內部程式有時可能必須使用迴路被執行當時的指標量,對單一層的迴路而言,迴路指標的引用指令,習慣上稱之為I,這個指令對FOR…NEXT型的迴路或DO…LOOP型的迴路而言,沒有差別,均可使用此同一功能的指令I,而I的內容則為R@的意思,也就是取用回返堆疊頂端之值,便可得到迴路執行當時指標之值。
但若有限迴路的使用不只一層時,單使用一個I指令便無法獲得各層正確的迴路指標值,必須設計相同意義但內容不同的指標引用指令,我們習慣上將其稱之為J、K、L、…等,它們的高階定義型式設計,依理,便是由回返堆疊中更深位置的單元來取得,J為第四個單元,K為第六個單元,L為第八個單元…。

6. 延伸性的應用

如果完全了解上述說明,就可以進一步設計系統中須要的其他類似指令了,例如:eForth32中缺乏從FOR…NEXT跳出來的類似LEAVE指令,用騙的,騙系統迴路指標已經是0了的設計,就是fig標準的LEAVE,如下:

: LEAVE-fig R> R> DROP 0 >R >R ;

被執行時,從LEAVE-fig至NEXT的程式還會被執行一次。要符合83標準的LEAVE應該是:

: LEAVE-83 R> DROP R> DROP ;

為了尊重Bill Muench只設計FOR…NEXT的個人觀點,我贊成別的系統也可添加。而Win32Forth中缺此指令,我們也不該厚此薄彼,放任其缺乏而不顧,我為Win32Forth設計它們如下:

: (NEXT)
R> R> DUP
IF 1 - >R ABS>REL @ REL>ABS >R EXIT
THEN DROP CELL + >R ;
: FOR ( n - - ) COMPILE >R HERE ; IMMEDIATE
: NEXT COMPILE (NEXT) APP-, ; IMMEDIATE
: I@ R> R> DUP >R SWAP >R ;

看懂了前文,這裡就不用解釋設計內容了,在我心中,迴路指令群的紛爭想法,到此也已天下太平,皆大歡喜。

7. 其他的檢討

也許有人根本就不喜歡eForth,比較起來,其他的FORTH系統性能比eForth好太多了,所以不想浪費時間在搞這個舊系統、舊學問上面,可能會認為此處留下的相關文章形同無用之物,沒有價值。
我倒認為,做為一個FORTH的傳承者,眼光要放遠,不要拘泥於一時的不得意,而心胸狹隘放棄發展,要知道目前的流行並不代表未來的世界,試想,如果微軟明天就收攤了,那一種FORTH才表現了永恆?
eForth還有很多的不好用處,我所用的eForth32甚至於沒有FORGET指令,每次設計錯了,都得BYE掉重來,要上XP系統,就得換跑Os2的版本,它的EDITOR要藉助於Microsoft Word來編寫才好用,缺與視窗的連結功能……..等等等,許多的不方便,讓使用者覺得真是不好用。也許只有我們從艱苦環境中長大的人才能忍受這許多的不便,但要知道,全世界公認最好的poly Forth系統,它在使用上也一直都有許多的不便,它卻依然賣得很好,顯然很多人買來做了很多事情,總不會一直有人花大錢買它,只是為了表示自己在FORTH領域的身份吧?
作者花費了許多時間在與eForth相關的系統上,主要目的是想驗證此系統是否真正具有一貫性,真有則更可貴,別的FORTH系統較不能存活久遠,eForth希望較大。證明的方法無他,就是用它來進行實際設計,每逢阻礙就予以突破,走到盡頭就知道它很可行,如此而已,我也身體力行了,證明了它很可行。
FORTH在軟體界號稱可像一個『窮人的原子彈』,我自己深信,個人、不花錢、猛搞一通、將它搞大了,確實可以轟轟烈烈的爆炸一場,就像在網路上大作戰一般,只是事在人為,宜不宜如此做而已。
網路讓全中國的中國人有機會來接觸FORTH了,我不知道這些同胞能不能像我們當年一樣吃苦耐勞的學通FORTH,然後用它來做可貴的事情,整個華人的世界,比台灣大太多了,相對的發展潛力也將非常可觀,我常寫FORTH相關文章,寄望的也就是華人的世界在這方面能有點作為。
剛開始搞FORTH的人,可以不要把事情搞得又大又複雜,我先翻譯了F83標準的中文指令集,因為它規模較小,提供初學者參考,意思是想告訴想進入這個領域的新人,先搞這些就很夠了,初看的人最好將其妥善保存。
將本程式加入eForth核心系統後,除了FORGET指令外,大致已符合F83標準,足夠使用了。回顧FORTH標準指令的發展,事實上從無、到fig、到79、到83、再到ANSI,彼此之間差異不是全面的,而且還很有限,學通FORTH就知道了。

2025年9月2日 星期二

理論與實際

理論與實際


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


子彈跟砲彈都不是飛彈,發射出去以後的飛行軌跡無法修正,它們的理論彈道是固定的,如果不考慮環境因素的影響,也不考慮彈藥質地的統計性差異,落點就是確定值。排除這些因素,根據理論公式設計砲彈射擊程式,是件趣味事情。

我從網上的社群網頁收集到這一張簡圖,它就在顯示砲彈彈道的理論公式,我拿它來設計程式,實際上的有用應用,應該還要添加以程式修正發射角度的能力。


我是職業軍人,只是專長比較特殊,從中正理工學院核子工程系畢業後,必須前往最基層的正規野戰部隊服役,我前往的部隊番號是陸軍第 57 師,這個師傳統上對軍官的要求比較嚴格。我駐防馬祖時,幹到 57 師連長後才考進中山科學研究院,看過軍史記錄,確認 1941 年 57 師打過長沙會戰,1974 年,連上還有經歷過長沙會戰的老士官班長。 57 師是輕裝師,新報到的軍官一律拔掉軍階,在幹訓班重訓單兵、伍、班、排之槍砲操練三個月,成績記入軍籍表。


以砲操為例,解釋理論彈道公式的用途,才能結合理論與實際來設計程式。

砲操的射擊口令是這樣喊出來的:

第一砲注意
12 點鐘方向
目標正前方機槍陣地
俯角 30 度
榴彈一發
裝藥兩包


戰場的口令都很簡潔,我們從口令的內容來解釋理論彈道公式中那些參數比較有用。

一門砲有裝填彈藥的士兵、操作瞄準具的士兵、射手。

聽到 12 點鐘方向口令時,砲口會轉置到正前方的大約位置。
聽到目標正前方機槍陣地口令時,瞄準手會進行精確的直線對準瞄準。
聽到俯角 30 度口令時,瞄準手會將發射砲口調整出俯角 30 度的角度。
聽到榴彈一發口令時,彈藥兵與裝填手會將一發榴彈砲彈塞進砲管發射座。
聽到裝藥兩包口令時,彈藥兵會塞進兩包炸藥包,關上砲栓。
聽到放的口令時,砲彈就發射出去了。轟的一聲,震耳欲聾。

現在回頭看彈道貼圖,砲彈射擊時,完成砲口直線瞄準後,確實只剩二維內的事項必須考慮,而且只須調整砲口的發射俯角單一個參數。

那一種砲彈、裝藥幾包,決定了砲彈的發射初速,這項決定由喊口令的砲長負責,砲兵只管操作。換句話說,每次發射砲彈,最後,只有一個參數需要調整,也就是發射俯角。

從實務的觀點來看貼圖,實際上彈道的軌跡,也就是 y 與 x 的關係,在沒有繪圖功能時,不太有用。我只令 x 等於最高高度之值來敷衍過去。

這樣理解砲操,就可以開始設計程式了。我設計的數學計算系統很簡明,彈道公式完全可以直接搬入程式。為了表示式能更清晰的顯示,幾個用到的三角函數數值,直接在輸入數據後就立刻執行,產生結果,所以我不吝於變數用量,直接宣告出 14 個變數的名稱來設計程式。

砲彈不是發射出去就算了,砲長直接觀察落點,或前進觀測官會回報落點與目標差了多少角度與多少公尺。砲長在發射下一發砲彈時就得修正涉及參數。修正的主值,除了直線瞄準外,仍然是發射俯角需要改為幾度。

於是,我再根據公式,增加設計了一個 adj 指令,這個指令可憑需要增加或減少落點的距離直接換算出新的發射俯角,提供的答案仍是砲口應該升或降的新角度。應該增加的公尺數用正值輸入,必須減少的公尺數用負值輸入。操作方式只用兩個指令,執行 main 指令時,只根據結果印出彈道相關之數據供砲長參考。需要修正發射俯角時,執行 adj 指令便可。程式附帶簡單的操作,列示如下:


\ shell.f

\ degree : 發射俯角(度度量)
\ z : 弳度量
\ T : 飛行時間 ( sec )
\ R : 炸點距離 ( m )
\ H : 最高高度 ( m )
\ y : 彈道軌跡 ( m )
\ x : 水平距離 ( m )
\ u : 砲彈初速 ( 250 m/sec )
\ g : 重力加速度 ( 9.8 m/sec^2 )
\ a : 調整距離 ( +/-m )

14 reals degree z u g T H R y x sinz sin2z cosz tanz a

: InputData basic
10 let { degree = 30.0 e 0 }
20 let { z = degree * f(pi) / 180.0 e 0 }
30 let { u = 250.0 e 0 } 
40 let { g = 9.8 e 0 }
50 let { sinz = sin ( z ) }
60 let { sin2z = sin ( f2.0e0 * z ) }
70 let { cosz = cos ( z ) }
80 let { tanz = tan ( z ) }
90 end ;

: main basic
10 run InputData
20 let { T = ( f2.0e0 * u * sinz ) / g }
30 let { H = ( (  u * u ) * ( sinz * sinz ) ) /  ( f2.0e0 * g ) }
40 let { R = ( u * u ) * sin2z / g }
50 let { x = H }
60 let { y = x * tanz - ( g * x * x ) / (  f2.0e0 * u * u * cosz * cosz ) }
70 run cr ." 發射俯角 = " degree f.  ." 度 "
       cr ." 飛行時間 = " T f. ." 秒 "
       cr ." 最高高度 = " H f. ." 公尺 "
       cr ." 炸點距離 = " R f. ." 公尺 "
       cr ." 砲彈初速 = " u f. ." 公尺/秒 "   
\ 70 print { z , T , H , R , u }
80 end ;

: adj basic
10 run cr ." 輸入修正距離為幾公尺? " cr
20 InputR a
30 let { R = R + a }
40 let { z = asin ( ( R * g ) / ( u * u ) ) / f2.0e0 }
50 let { degree = z * 180.0 e 0 / f(pi) }
60 run cr ." 修正射角成為 " degree f. ." 度 " cr
70 end ;

hing@ctt:~$ ./l

AMDX86 ciforth 5.3.0 
fload shell.f
g : ISN'T UNIQUE                                                
A : ISN'T UNIQUE                                                
 OK
main

發射俯角 = 30.0度 
飛行時間 = 25.510204081秒 
最高高度 = 797.19387755公尺 
炸點距離 = 5523.1211976公尺 
砲彈初速 = 250.0公尺/秒  OK
adj

輸入修正距離為幾公尺? 

? +230.0 e 0

修正射角成為 32.21704191  度 
 OK
adj

輸入修正距離為幾公尺? 

? -45.0 e 0

修正射角成為 31.756360366  度 
 OK

2025年8月16日 星期六

飲水思源追溯 BASIC 程式語言

飲水思源追溯 BASIC 程式語言


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


慣用電腦上網看書,很久沒去過圖書館,我家附近就有圖書分館,但沒光顧過幾次。有幾個原因不去:
一是館中沒有什麼我願意看的書,小說居多,還都不是名著,是暢銷小說。
二是老了,視力不好,看書有點吃力,網上瀏覽的資訊,能自由放大縮小,都可以調整到滿意為止。
三是我不喜歡讀書期限被限制,借閱時間到了就得還書不自由,真有需要的書,我寧可自己買來用。

這半年來,我陪著小外孫長大,從脖子還需要扶著開始,現在剛滿一歲會走路了,我們每星期花四天,以他為主的方式過著生活,我搞創作受到影響,吸收新知則不受影響。

七月中,因為天氣很冷,外出時只宜在室內活動,我帶著小外孫在圖書館內走動,順便看看架上書籍,見到有兩本談論 Raspberry pi 的書籍,毫不猶豫的就借回家看了。不幸,是過時的書籍,介紹到 pi 2 而已。書籍我還是快速讀了一遍,想知道 Raspberry pi 的輸出/輸入機制,結果,沒有幫助,書中採用了兩種程式語言寫範例程式,一是 Python ,另一是 BBC Basic 。他們執行輸出/輸入的方式都得另外加裝驅動軟體,執行方法也只能跟著規定方法使用,不能更動。這樣的用法,連硬體實質位址的訊息都得不到,我很失望的把書還了,真想用 Forth 設計輸出/輸入程式的話,得另外設法從網上學習獲得硬體位址訊息,我估計只宜透過組合語言之類的程式才能從軟體訊息中受益。

開卷都是有益的,我也是讀了過時的舊版書後,才知道 Raspberry pi 電路板上有可以進行硬體重置(Reset)的開放式接點,只是出廠時都未安裝按鈕而已。在 pi 2 的舊書中提到,接點在 HDMI 影像輸出插座旁邊,有文字標示。我的板子是 pi 3 與 pi 4 的版本,找不到這個接點,但在 40 腳的 I/O 插座旁邊有一個標示了 RUN 文字、露出銅泊的兩點,可以焊接重置按鈕。於是,我試用了這兩點,待系統啟動後,我只用迴紋針插在一個洞口上,板上電壓立刻就被降壓下來,重置了。看來,不用再裝按鈕,想重置時,小心以導電金屬碰一下接點就能達到目的。由於單板電腦確實經常會碰到當機情況,需要重置系統,此前,我是買了帶有開關的插座,採用直接切除電源的方式來解決問題。看了舊書,得了訊息,自己親自動手做過幾遍,重置問題就解決了。

pi 2 的舊書中展示了 BBC Basic 系統,我也想加裝,但 pi 3 與 pi 4 的板子都不提供這個系統的下載安裝服務。我試用通配字元星號(*)置於 basic 前面,訊息顯示有 *basic 可安裝,是 bwbasic ,叫 bywaterBASIC 。安裝需時稍久,因為安裝時必須對系統進行組態設定的關係,我耐心的看它正確無誤的執行出結果。倒過頭來,我順便也為作業系統是 Ubuntu 20.04 的桌機也安裝了 bwbasic ,幾乎是瞬間就完成了安裝。裝的全都是 V2.20 的版本,網上已有 V3.0 的版本,但缺人手協助系統進行組態設定的設計。我只下載了源碼,沒有安裝。從 GitHub 中顯示的訊息可以看得出來,這個 bwbasic 是五年前貼網的,換句話說,它能免費下載安裝的時間也只有五年而已,還算新。

在個人電腦的領域,我最早接觸的程式語言就是 BASIC, 那是 1982 年時在台灣使用之 TRS-80 個人電腦的 BASIC ,已有浮點數計算功能。它確實是影響了我一生使用電腦的走向,尤其是後來我透過 Forth 系統設計出 ABC Forth 的研發過程,完全是根據了早期使用過 BASIC 程式語言所獲得的經驗在設計系統。中國人講求飲水要思源,追溯最早的根源,是件有意義的事情,甚至於我還能強調,我的電腦個人筆記已有 20 幾本,第 1 本中,開頭就記錄了 TRS-80 BASIC 的操作步驟。80 年代,我在核能研究所工作期間,曾經在夜間開辦過教導員工使用 BASIC 語言設計程式的課程半年,學完者發給中華民國 FORTH 語言協會的結業證書,全球有效,因為當時我們是 Fig 總會的會員組織,有許多人在美國有效使用過這樣的證書。

熟悉 BASIC ,我才能自己設計 BASIC 。
不尊重前人的創作,就不可能有後來的發展。
我仔細鑽研過 BASIC ,ABC Forth 才能比較健全的擁有 BASIC 的性能。
我用它來進行複雜的數學計算程式設計,也用它完成許多數值分析方面的研究。
程式語言的數學表示式,BASIC 的語法最好。
我用 Forth 的編譯後直接執行之精神,解決 BASIC 因係一邊執行一邊編譯而跑得太慢的問題。
我用 Forth 自然叫用已經存在之前程式的精神,解決 BASIC 被詬病為麵條式程式碼的缺點。
我用 Forth 之延伸性,發展出可以處裡任意一種數系的 BASIC 程式寫法。
還有許多特點,都是這樣發展而成的。

bwbasic 源碼的現行貢獻者叫 Ted Campbell ,據他自稱,這個以 C 語言設計出來的 bwbasic 最原始的作者是他的祖母 Verda Spell 於 1982 年設計的。他還提到謝謝 Mr. David MacKenzie 協助了安裝時對作業系統進行組態設定的程式設計。這樣的作法展現了尊重前人的技術傳承方式,我也感謝最原始的創作者。我的三台 Linux OS 電腦都裝好了 bwbasic , 有空時,我必會試用 bwbasic ,因為我熟 BASIC ,可以直接設計程式解決問題。

可以自由下載 bwbasic V2.20 版本源碼的網址如下:

https://salsa.debian.org/debian/bwbasic

它雖與自動安裝 bwbasic 的操作無關,留下這份源碼的資料,將來想要更動系統改成倍精度運算或其他規格時會有用途,只是您必須熟悉 C 語言以及組態設定檔如何運作的方法才能辦到。安裝 bwbasic 的操作,簡單到只須輸入下列文字:

$ sudo apt-get install bwbasic

今年,Forth 被號稱是已有被使用了 55 年 ( 1970 )的歷史。 BASIC 與其他知名程式語言的年紀則如下圖所示,僅供參考。飲水思源就在思考這些根源。





今天,我們能夠這麼方便的書寫中文創作文章,必須感謝中國人的祖先創造了中文的文字,它的起源可以追溯到幾千年前獸骨、龜殼上的甲骨文,與刻在銅器、陶器上的印記,還有石材上的碑帖,這些東西都是中國經過了千年流傳下來的寶物。至於幾千年前當代的文字傳承,可以簡單到窮人家只靠柳條在沙盤上寫字來教育下一代。我們從小都聽過亞聖先師孟子的母親為了教育孟子,曾經『孟母三遷』才找到適合之教育環境的故事。兩千多年前,孟母就用柳條沙盤教出了能成聖人的子女。沒有文字,那就難辦到了。

紙張還沒有出現以前,中國人主要是靠竹簡或木牘來傳承文字。最近環球時報的新聞報導了一則清華大學對戰國時期大量簡牘文本進行系統性研究後的成果,這些文本是清華大學於2008年7月獲得的戰國時期(公元前475-221年)竹簡的一部分。這批寶藏包含70多部古代文獻,其中60多部已佚失兩千多年。這些文獻涵蓋了哲學、歷史、天文、法律、數學和醫學等眾多學科,為了解早期中國文明提供了寶貴的資料。

研究還發現,大家都以為標點符號是現代發明的,但在這些竹簡中,我們清楚地看到了2000多年前就有人使用標點符號的證據。這些竹簡上的標記,能幫助古代讀者理解論證文章內複雜敘述之結構和含義。清華大學飲水思源式的簡牘文本研究,給中國文字帶來許多從未有過的創新見解。隨文附貼一份在湖北出土的典型竹簡,它們是中國文化中的寶藏。




飲水思源是對前人創作的尊重,不只是中國人該如此,全世界人人都該這樣。最近我在 Twitter(X) 社群媒體中見到一個帳號的貼文串,出現了西方人出言不遜侮辱阿拉伯人的訊息,強說阿拉伯人沒有科學貢獻。結果,阿拉伯人豪不客氣的連貼幾則漫畫,教訓西方人。

要知道,數學是開啟科學之門的鑰匙,而整個數學的根基是代數與幾何,阿拉伯人發明了代數與幾何,甚至於阿拉伯數字,誰有資格嘲笑阿拉伯人對科學沒有貢獻?

飲水要思源,任何科學都該感謝阿拉伯人創建了數學的源泉,科學才得以發展。數目字中,連零的觀念都是來自阿拉伯人的數學創舉,下列漫畫能告訴你阿拉伯數字是怎麼形成的?有『數目之量』與『幾何夾角』的意義在其中。你飲了甘泉,要感謝源泉啊 ! 別再瞧不起阿拉伯人。




2025年8月2日 星期六

舉一反三

舉一反三


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

這是一句所有中國人都耳熟能詳的四字成語,不只是因為它用字簡單,它還是從兩千多年前就開始被中國人使用於教育及學習環境的鼓勵用語。

論語第七章述而篇中有一條孔子的敘述:子曰:「不憤不啟,不悱不發。舉一隅不以三隅反,則不復也。」

它很能說明這就是舉一反三成語的起源。

有過不少賢明的作者彙整出許多有用的成語出版成書,我們在學生時代就買過這種書。例如:『分類中國成語大辭典』。那個年代,電腦還不普及,更沒有網路可以使用,這本有助於自修、作文、書信、寫作的參考用書,是值得花錢買來終生使用的。我買過,很不幸,買書的時候沒有料到平裝書不耐環境的摧殘,用了五六十年的這本書,再翻幾次,封面、封底都得粉化了,就算這樣,我仍然珍惜。為了遣詞用字時少出差錯,也為了閱讀文章時能夠深刻了解且不搞錯文章的意思,經常查找成語工具用書是必要的。閒暇想看書時,成語辭典後面也附有成語故事,看故事書就是享受,也帶有修身養性的意義。

成語大辭典論述舉一反三的短文故事中,鼓勵大家掌握知識、靈活運用、發展知識。這種信而好古的精神,確實是學習的準繩,也是中國文化歷久不衰的主因,有太多的古聖先賢致力於傳承,我們才享受了文化。

我在學習、研發 Forth 的過程中沿用了同樣的方法鼓勵自己,舉一反三是經常做的事情。當然,舉一後不一定非反出三來不可,能夠反出一來也夠有益了,能反出大於三的結果來則是常有的事情。近幾年,我花了不少時間參與了幾個社群媒體的活動,幾乎天天都發表言論。但說實在的,活動產生的真正樂趣,來自別人有正面意義的網貼,我瀏覽它們的程度幾近於天天學習,看到好的題材,必定情不自禁的想方設法節錄下來,留參的多,轉發再炒作的比較少,就算這樣,我貼文的數量經常是高於常人。

最近有個數學帳號的網頁貼出了這麼一個簡單的訊息,訊息很普通,人人能懂,它卻激起了我寫程式的興趣,我用程式證明它。


ABC Forth 可以把程式寫得很簡單,只這樣做只算是舉一反一,我想反二時,問了自己: 64 位元的整數系統能算到多大的數字才會超出單整數能夠表示的範圍? 我寫了程式,得知只能算到 42 。

我還可以反三,能夠設計雙整數的最大公約數與最小公倍數,這樣就能擴大表現範圍,但我沒有這樣做。我跳過了反三,改搞反四,因為我設計的系統能夠處理無限位數的大數字,為了舉一反三而核對已經完成的系統,確定我並沒有為大數系統添加 BigBCD, BigLCM 兩個指令。所以,反四的取向,轉為設計大數字的最大公約數與最小公倍數。

在大數運算系統中,可用位數是可調變的,卻因此而必須先行宣告來決定可用位數,就是因為這樣,我在設計大數系統時才沒有把 BigBCD, BigLCM 兩個函數固定在系統中。

以下是今天寫出來的程式與執行結果:


 

3 integers i n tlcm

: imain 
basic
10 inputi n
10 let tlcm = 1 
20 for i = 1 to n
30 let tlcm = LCM ( tlcm i )
40 next i
50 print tlcm
60 end ; 

0 BIGVARIABLE A 1000 ALLOT
0 BIGVARIABLE B 1000 ALLOT
0 BIGVARIABLE A1 1000 ALLOT
0 BIGVARIABLE B1 1000 ALLOT
0 BIGVARIABLE C1 1000 ALLOT
0 BIGVARIABLE A2 1000 ALLOT
0 BIGVARIABLE B2 1000 ALLOT
0 BIGVARIABLE C2 1000 ALLOT
0 BIGVARIABLE BGCD 1000 ALLOT
0 BIGVARIABLE BLCM 1000 ALLOT

: InputData 
BASIC
10 LET B{ A = S" 12193263112482853211126352690 " S>BIG }B
20 LET B{ B = S" 440370366363 " S>BIG }B
30 END ;

: GCDLCM 
BASIC
10 LET B{ A1 = A }B
20 LET B{ B1 = B }B
30 IF B{ A1 < B1 }B THEN 50
40 GOTO 60
50 LET B{ C1 = A1 }B :: B{ A1 = B1 }B :: B{ B1 = C1 }B
60 LET B{ C1 = A1 MOD B1 }B :: B{ A1 = B1 }B :: B{ B1 = C1 }B
70 IF B{ C1 <> BIG0 }B THEN -60
80 LET B{ BGCD = A1 }B
90 LET B{ BLCM =  ( A * B / BGCD ) }B
100 END ;

: BMAIN 
BASIC
10 RUN InputData
20 RUN GCDLCM
30 PRINT " Big GCD is : "
40 RUN BGCD BIG.
50 PRINT " Big LCM is : "
60 RUN BLCM BIG.
70 END ;

\s
imain

? 10

                2520  OK
imain 

? 42

  219060189739591200  OK
bmain

Big GCD is : 
9 digits 
370370367 
=========1=========2=========3=========4=========5
12345678901234567890123456789012345678901234567890
=========(c) 2018 Copyright, Bottom Ruler=========

Big LCM is : 
32 digits 
14497789840742112468029233348410 
=========1=========2=========3=========4=========5
12345678901234567890123456789012345678901234567890
=========(c) 2018 Copyright, Bottom Ruler=========
 OK

2025年7月16日 星期三

系統性能探討

系統性能探討


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


有許多規模比較龐大的程式,我沒有發表在這個網頁,純屬於我個人的應用程式者也不網貼。有些論題很適合用來檢討系統性能,因為它們的內容通常涉及一些比較特別的需求。這一次,我取用傅立葉轉換(Fourier transform)的技術來探討我設計之 ABC Forth 系統的一些性能。

網上已有很多傅立葉轉換技術方面的資源可供參考,我也不例外的大方採用,用到之處會說明來源,以尊重他人的創作。本文借用的貼圖來自不同的網頁,表示函數時的參數並不一致,這不是我所強調的重點,但很容易看懂。別人貼出的材料都是經過一番努力後的貢獻,所以不要太強求。

Twitter(X)社群媒體上,有一個名稱為歷史上的物理學(Physics in history)帳號,最近貼出過一份能夠精簡說明傅立葉轉換技術的照片,我轉貼於此處與大家分享。這方面的數學,要在大學二年級以後才學,它是專門用於信號處理的工具,也可以用來作為偏微分方程式(PDF)求解時的工具。它的完整數學表示式定義,照片中有。


以物理觀念更簡單的說明,就是傅立葉轉換,能把隨時間而變化的函數轉換成隨頻率而變化的函數,函數的本質不變。

我自己在工程實務上的應用,除了寫過大量信號處裡方面的相關程式外,就是曾將此技術應用於原子爐六台 600 匹馬力水泵兩公尺長轉軸各有四個軸承的長期監測上。當軸承安裝妥當後,以頻譜顯示儀獲得的頻譜,是只有很單純的一個尖峰。當軸承出現問題,例如是彈子盤保持器有微小破裂時,以頻譜顯示儀獲得的頻譜,是單一個的尖峰不見了,改以佔用頻率範圍很寬廣的許多低峰值圖譜出現。這個時候,運轉裝備必須停機更換軸承,否則直徑兩英吋的傳動軸就會被震斷,整套水泵連帶馬達都全毀,重裝新泵時,連地腳螺絲都得重新設計,損失非常大,我經歷過。量測元件獲得的訊號通常都是時變訊息,要想獲得對應的頻變訊息,就需要傅立葉轉換技術。貴重與安全要求較高的裝備,值得加裝這種監測系統,能防患於未然。

我大三時學過偏微方程專門課程,書中滿滿的傅立葉轉換應用,印象深刻。我從事過電腦信號處裡的研究工作,參與過台電核二廠雜波分析發展計畫,也參與過魚雷相控陣列聲納的電腦技術研發計畫,有過一些信號處裡方面的實作經驗。設計研發程式時,知道信號處裡軟體技術發展時講求的重點,但那時還沒有自己設計過系統。也因此在後來自己設計數學計算系統時,就會強行設計一些以前系統所沒有提供的性能。例如本文提到的複數運算系統,可以直接在複數體系觀念之下設計程式。

任何週期性的時變函數圖譜,都能由許多個不同週期的函數圖譜合成,這是一種線性關係(函數乘以常數後相加本質不變的函數關係),以簡圖表示就能看出,從時變轉換成頻變表示時,函數本質不變的道理。


我把細部強調式的傅立葉轉換數學式再貼一遍,此圖仔細說明了數學式中每個符號的意義。


電腦工程實務上,這樣的公式被改寫成離散傅立葉轉換(DFT)的數學表示式:


傅立葉轉換說明至此,不再深入,有興趣的讀者可以自行從網上學習。

接下來,我採用維基百科與 Rosetta code 網頁共同建議的快速傅立葉轉換(FFT)程序描訴虛碼來探討系統性能。資料來源網頁為:

維基百科自由百科全書(WikipediaThe Free Encyclopedia)

https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm


我們逐列探討能直接執行這套虛碼的系統規格。

首先,第一列的說明強調要用到以 2 為基底的對數函數。
我在設計系統時,連浮點數的對數函數也全為採用以 2 為基底的根本函數來推導出其他所有的對數函數。但是以數學體系的觀念來設計系統時,整數與浮點數是不能混著出現在同一列運算表達式之程式中的,因為系統中兩者的資料格式完全不同。所以,在整數環境中,必須專為整數體系建立它自己的以 2 為基底的對數函數。我設計的系統中有此函數,名稱就叫做 nlb ( n1 -- n2 ),以對應於以 10 為基底的整數之對數函數 nlog ( n1 -- n2 ) 。

程式設計方法一開始就強調時變輸入信號 A 數列的點數必須為 2 的整數方次,例如:2 的 10 次方就是 1024 點,且輸入信號必須為複數的格式。我設計的系統中可以直接宣告產生陣列,所使用的指令是 [ARRAY] 。指標可以從 0 開始,這一條件,也是這套虛碼程序設計方法中的要求條件之一。

描述方法中有一列在計算複數的 exp 函數,它的參數必須是複數,結果也必須是複數。我設計的系統中建立了這樣的函數,在純粹 Forth 的環境中,我仍沿用 Forth 愛用者的習慣使用 zexp 的函數名稱。但在 BASIC 環境中,我直接就採用 exp 的設計,這樣設計不會搞混系統,因為浮點術與複數的資料格式不同,在同一列運算程式中只能有一種數字格式,所以複數環境仍可以照樣採用 exp 為該函數的名稱。

描述方法中只有指標需要運算之處是我設計系統時沒有實現的設計。這裡有兩個數列的指標 (k+j+m/2) 與 (k+j) 需要進行整數運算。在正整數運算之計算落定後,資料格式必須是正整數,這在整數環境內,不經設計就能直接執行得出來。但是在浮點數、複數等的非整數環境內就不能直接執行出來。要設計出這樣的規格時,必須在運算列內增加改變運算規格的暫態處裡,待指標之整數運算結束後,還得改變回來。我嫌這樣設計起來雜亂無章,只在 Win32Forth 系統中實現過設計後,便放棄了這樣的設計。解決辦法很簡單,凡是遇到指標有運算的地方,就必須在前一列直接增設一列程式。例如上述情況就寫成:


   
10 let k+j+m/2 = k + j + ( m / 2 )
10 let k+j = k + j


此處討論的陣列指標之運算,是快速傅立葉轉換(FFT)技術的關鍵部份。能這樣運算後調整陣列輸出數據指標,將計算所得放置到指標指定的位置,是快速傅立葉轉換能快的主因。研究 FFT 技術的主要工作,就是在探討如何處裡指標才能減少運算次數,以便加速獲得數位訊號整體的轉換結果。每種方式的技術細節,當初都曾被寫成博士論文而發表。這種技術就適合拿來檢驗系統的性能。

沒有其他限制了。換句話說,我設計的系統能夠直接書寫複數運算式子,設計好的程式跟虛碼所描述的步驟會很接近,系統有此特點。

輸入與輸出數據上了千點以上,很難再單憑人工輸入每一點數據的方式來完成工作了。我設計的系統因此而安排了可以簡易執行開關檔案、自動進行數據存取工作的能力。這一點很重要,遇到產生結果需要藉由別的軟體實現複雜工作時特別有用,過去的貼文中已有很多這樣的使用範例。

在套用這種功能時,有一點必須注意,如果數列只是單一個變數的數列,容易處裡。數列每列具有兩個以上的變數數列時必須注意 ! 按照 Forth 處裡輸出輸入數據的方式,一次只做一列,遇到跳列(LF或CR)時可以暫停。但輸入的數據在堆疊上是先進後出,輸出的數據是後進的先出。我在 Win32Forth 增建 ABC Forth 性能時,解決過這方面的問題,後來再增建的系統就簡化了設計,不再理會這種問題,特此聲明。

2025年7月1日 星期二

弦外之音

弦外之音


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


這一次,我不談程式,改談 Forth 的哲理。哲理的範圍很廣,我縮小到只談 Forth 系統變數(user's variable)。我也不談各個系統變數的定義,只談整體的意義。

初學 Forth 的人在基礎教材中見不到這方面的論述,基礎教材通常只教標準指令的用法。也不談 Forth 的系統架構,因為各個 Forth 的系統架構大都不同,難談。

如果您想把 Forth 學得很好,將來想要大肆利用 Forth 發展出自己的抱負,Forth 哲理中的每個項目就都應該搞得清清楚楚。系統變數只是眾多哲理中的一個項目,它有必須存在之宏觀的理由,個別系統變數都有必須建立它的道理。這種哲理看似是在學習 Forth 時的弦外之音, 實則是使用者必需學會的基本觀念,碰到它時,不要逃避,不要排擠。

我們在 1984 年 4 月創辦的『FORTH期刊』p.19 中就有一篇諶其騮作者所寫的『FORTH系統變數簡介』,該文可以提供永久性的參考。即使後來其他系統的系統變數已有大量的變化,系統變數的使用觀念則沒有改變,是系統所需,也是使用者設計程式時經常非要用到不可的東西。

系統變數有幾項特色。

首先是通常被集中處裡,除了能讓系統結構整齊外,它另有幾方面隱含的性質。

古老的 Forth 強調多工(multi-task)性能,有少數幾個系統變數就是單工被執行時非得獨立起來使用不可的單套變數,例如:兩個堆疊指標 S0 及 R0 就是。它們都是能讓工多而不亂的變化根據。在 Forth 系統中,所謂的一工,可能只是一個程式,也可以是一位使用者(user),Forth 也因此把系統變數稱為使用者變數。

Forth 系統具有可以長出標的系統(target system)的固有性能,但不是每個系統的作者都能將這種性能設計得出來。所以,我在挑選可用系統時,會將這項要求列為首要考慮條件,以便自己長期努力發展出來的成果能被永久性的固定下來。Forth 系統無論是在初次以各種其他工具語言發展出來,或者是就憑祖代系統憑藉蛻變編譯(meta compile)能力產生新的子代系統,在系統長成的最後階段,都得對系統變數進行最後的調整修訂,如此,子代系統運行時,才能有正確的系統變數內容可用。

系統變數的放置方式有秩序性,不可前後隨便對調,這也是多工設計時的一項簡明要求。Forth 系統內的自有多功能力,在規劃出新的一工(task)時,第一個記憶體單元放的是此工是否要睡著(sleep down)不被執行時的跳躍指令,跳到下一工去。否則就該醒來(wake up)執行本工的第一件處置工作,那就是找到自己的回返堆疊指標,在那裡取得執行碼。因此,緊隨是否該跳躍的指令碼後,第一個系統變數就放此工總系統變數的指標,叫 U0 ,然後,緊隨著的就是 S0 和 R0 ,這種秩序絕不能變。

系統變數通常有幾十個,以現行的 ciForth 為例,Fig-Forth 是它的前身,系統變數大約有四十個。這些系統變數又可化分成兩大類,一類是多工狀況執行時要求至少必須獨立分出來的系統變數,在 ciForth 中選出了十一個。另一類系統變數則是分工程式被執行時還必須與它工共用的系統變數,成立新工時,就可以不必另外宣告出記憶體空間來放置此類系統變數。因此,成立新工的宣告過程,必定包含了需要宣告出多少個單元來存放該工之系統變數的用量。

傳統的 Forth 多工,是根據一種被稱為知更鳥輪唱(round Robin)法設計出來的多工方式,跟現行作業系統固有的多工運作方式不同,現行作業系統採用一種所謂的先佔式(temptative)多工來運作,那一工先被喚醒就先執行那一工,多 CPU 的時代,不再熱衷於傳統 Forth 的多工設計,逐漸地,大家也不設計傳統的多工程式了。

我們在 32 位元的 Win32Forth 系統中還能見得到純用 Forth 設計出來的多工範例,在 64 位元的 ciForth 中沒有這方面的需求。我需要時,也改採 Linux OS 中的多工方式,直接讓 Forth 去叫用另一套 Forth ,執行出另一個工。這在本網頁的貼文中可以看到好幾個使用範例, 凡是有程式執行的展示影片,同時出現有背景音樂的短片內,都依靠用 Forth 叫用另一套 Forth 執行音樂播放之先佔式多工而完成設計。

ciForth 是一個可以自行長出標的系統的好用 Forth,它在處裡出長成的標的系統時,有一段處裡系統變數的工作必須要做,這樣,整個系統的可執行碼檔案中才能固定出新的系統變數規格,這是一個很有效率的作法,卻不是其他 Forth 系統能辦得到的設計,是作者熟悉 Linux OS 使用環境後的創意設計。也請注意 ciForth 在各種作業系統中的版本,並非全部都具有此項功能,微軟的 Wina64 就不具有,沒有此項功能的系統我就不用。

能讀通系統源程式碼是件痛快的事情,看似弦外之音的系統變數之形成哲理大有意義存在,講述如何研究它? 則超出了貼出一個範例程式的範疇,只能專文討論。我在設計 ciForth 的 ABC Forth 數學計算系統時,動用到它,修改過執行 >NUMBER 時必須隱性影響 DPL (第 20 個系統變數)的方式,特貼此文說明。

黃河

中國所有的兒童都聽過『大禹治水十三年,三過家門而不入』的故事,也曉得故事的意義在強調工作時要用對方法,要堅忍不拔的完成工作,這是四千多年前流傳下來的故事。

大禹是夏朝的第一個皇帝(在位七年,時間為西元前 2205~2198 年,另有在位 27 年之說)。這個故事從此告訴了後人,黃河經常氾濫成災。古人形容邪惡帝王的惡劣統治就像洪水與猛獸,所指的洪水,就有如黃河的氾濫。

中國歷史上,黃河在過去的三千多年內共有高達 1,593 次的氾濫記錄,氾濫使其主幹道改變了 18 次;每次的泛濫也都造成大量傷亡及經濟損失。

近代的四次記錄則為 1410 年黃河氾濫, 1887 年黄河决口, 1921 年宫家决口, 1958 年黄河洪水。

1900 年以後的兩次災情記錄為:

宮家決口也稱宮家壩決口,是民國時期 1921 年 7 月 19 日發生在中國山東利津宮家的一次黃河決口。決口後水災影響到利津、霑化、濱縣、無棣四縣,災民 18.4 萬餘人,受災面積 5400 平方里。

1958 年黃河洪水指的是 1958 年 7 月發生在中華人民共和國黃河中下游地區的水災。此次水災淹沒村莊 1708 個,淹沒耕地 304 萬畝,房屋倒塌 30 萬間,京廣鐵路中斷 14 天,受災人口達 74.08 萬人。據中國政府官方資料顯示,河南、山東兩省出動約 200 萬人進行防汛,確保了黃河下游的防洪安全。

黃河氾濫的起因係上游大量沖積下來的黃沙,沉積在下游出海前的河床上,長久下來,河槽萎縮,造成河床高於地面的懸河現象,直到河堤受不了而潰堤,於是發生規模宏大之河水氾濫的洪災,這是定性的說法。最近我看到中國黃河教育電視台的科普教育新聞報導中,提供了一些近代的定量數據,可以讓大家更深刻的了解黃河。

根據科普新聞報導,現代的黃河,平均每年有 4 億噸泥沙堆積在下游河道裡。下游「地上懸河」現象長達 800 公里,最大懸差 10 公尺。

大禹治水能夠成功,用對的方法是疏導而非築堤圍堵,今人治理黃河,當然也只宜疏導。問題是年產 4 億噸的泥沙可不是個小量的數字, 800 公里的 10 公尺高度是個驚人的河床,就算動用南海吹沙填海的工程機器來幹此活,可能都不是一件輕鬆的工程,因此,最好的疏導方法,仍是讓沙流入海。

中國科普報導則介紹了一種借力使力的方式調沙,利用上下游水庫間歇性的突然大量放水,便可借大量的挾沙水流沖洗河床,這種調水調沙的方式可以清除河床積沙入海。今年一年已進行了 26 次,可以沖掉 21 億立方公尺的沙,讓河床平均下切了 3.1 公尺。

文末附貼一部陝西電視台製作的壺口大瀑布記錄短片顯示了大量的黃泥水,另附黃河下游廣闊的河道照片與幾張三門峽水庫與小浪底水庫調水調沙時的壯觀景象。在來沙的源頭執行沙漠造林、建造幾萬個淤沙壩有利於水土保持,中國做得到,最後展示一張在這一方面成就明顯的照片。

2025年6月15日 星期日

文化傳承

文化傳承


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


Forth 是一種程式語言,比較好且適當的中文名稱是『符式』。簡明的解釋,就是其標準指令中使用了許多符號作為指令名稱。這樣用,有其原因,因為它是即時性控制語言,標準鍵盤上的符號用來當指令,更可以快速的執行出結果。

語言都會形成文化,Forth 亦然,它有它的文化。中文的 Forth 也有中文方面的文化,我用了它 40 幾年,比較熟悉它所形成的中文文化背景。用到後來,我發展出了數學計算系統,年紀大了還在用,於是產生了想要傳承它的使命感。跟中國文化傳承者的情懷一樣,傳承 Forth 中文方面的文化,是老來還能用它設計系統者應有的使命。

40 幾年來已有不少跟 Forth 有關的中文書籍或翻譯書本出版,台灣、中國大陸都有,但內容都偏重於最基礎的 Forth 使用教學。英文版本的書籍,有一些內容會涉及系統設計,但數量很少。用 Forth 設計出系統的公司,為了營利賺錢,不可能完全公開設計內容,網上能獲得源碼的檔案,都只有基礎之裸系統。因此,傳承屬於與 Forth 有關之系統性質的文化,是很可貴的工作。尤其是這種文化必須經過實際上使用過後的嚴格考驗,實做成功後,才能講得出它們的道理。可是,這種文化不會在基礎教學的書籍中出現,因為層次比較高,不容易介紹。

這一篇文章舉一個很簡單的例子說明:

應用 Forth 系統,不能自外於整個世界,也不能自外於另一個 Forth 系統。本網頁中刊出的題材,最可貴的地方,就是已經刊出過許多 Forth 與外界溝通的範例程式,讀者在別的場合很難見到,所以可貴。Forth 系統與外界溝通的方法都有侷限性,溝通方法會隨時間與背景環境的不同而不同,這種性質令系統設計者只能隨時間與環境而跟著調變,沒有規則可循。但是一種透過公稱方式的溝通慣例可以討論,那就是透過生成或接受『檔案』(file) 的方式來溝通。設計這種溝通技術,仍需要不少有關檔案格式的基本觀念,它超出了 Forth 的討論範疇,短文也不可能說得清楚。用法則需要先行建立起固定的使用協定(protocol)才能設計,也就是說,使用步驟絕非一步到位,有固定死的步驟才能完成設計。我再簡化舉例,只討論 Forth 系統輸出資料時需要進行什麼設計?

從檔案輸入資料進 Forth 系統的觀念比較直接,只要知道開啟了的檔案之起始位址,剩下的事情就是只須根據這個位址來進行資料處裡的程式設計。

從系統輸出資料到檔案,根據我多年的使用經驗,比較好的方法是必須找出所有 Forth 執行輸出時相關的最根本指令,然後令其執行內容改為被轉向到輸出至另外的緩衝區,待資料輸出工作結束後,才一次性的將緩衝區內的資料轉存進指定檔案。我歸納出最根本的通用指令,顯性的有三個 : TYPE, EMIT, CR 。 隱性的另有兩個 : LF, EOF。隱性的,只須適時適地的安放其碼就能完成工作。顯性的三個指令則需採用高階的明碼設計,內容並不複雜,只須記得 TYPE 是印出一大串字串,後二者則只印出單個字元,如此便可。而設計的關鍵,則在必須將這三個顯性指令規劃成為可改變執行內容的向量式(vectorized)指令,在資料被存進檔案後,系統才能據此而恢復成原來的標準輸出狀態。一套典型的相關設計如下:


 
0 value OriginalTYPE
0 value OriginalEMIT
0 value OriginalCR

: SaveTypeEmitCr
  ['] TYPE >body @ to OriginalTYPE
  ['] EMIT >body @ to OriginalEMIT
  ['] CR   >body @ to OriginalCR
;

: RecoverTypeEmitCr 
  OriginalTYPE is TYPE
  OriginalEMIT is EMIT
  OriginalCR   is CR
;

: TYPE>PAD ( addr len -- )
  PAD +PLACE ;
: EMIT>PAD ( c -- )
  PAD c+PLACE ;

: PAD>FILE ( -- )
  PAD COUNT Fadr Flen + SWAP DUP +TO Flen MOVE ;



每次傳承一個文化主題時,內容不需要多,要點是要有重點。

我也注重中國文化的傳承,每次提及中國文化時,只須提出一個簡單的要素,能將罕有的要素傳揚到全世界才是重點。

最近,我在環球時報新聞網頁看到這樣的照片,覺得珍貴,刻意將其轉貼此處,謝謝該新聞媒體的報導。

這是一幀取材自中國河南省鞏義市永熙陵旁之麥田裡的照片,永熙陵是北宋第二個皇帝宋太宗(西元976年~997年)死後葬身之地,今人已開墾成麥田,但保留了千年石雕之地上物, 照片顯示的場景頗為撼動人心。

我刻意介紹的這份中國文物,不是宏偉壯觀的建築,也不是華麗驚人的遺物,只是中國北方麥田裡的千年石雕,看到後卻令人發思古之幽情。我轉貼來跟全世界分享,也與大家分享我設計系統、解決問題時的一份小程式及其重要觀念。


2025年6月1日 星期日

ARM 使用環境之經驗記錄

ARM 使用環境之經驗記錄


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

通常,我在試用 Forth 系統之發展末期,大約都是以能夠發展出數學函數的繪圖功能才告一段落。這次改到 ARM 使用環境之發展亦然,在我實現舊有的題材產生出同樣結果後,發展可以告一段落了。

前文曾經強調過,我的 ARM 發展環境就是 Raspberry pi3 與 pi4 ,以下就簡稱 pi3, pi4 。發展的過程中有許多經驗應該記錄下來,以防日後再使用它們又出問題時會是重蹈覆徹。

這種單板電腦套件(kit)沒有裝設電源開關,由於發展過程中時常要面對當機問題,我不得不為使用裝置添加開關。紐西蘭的壁上插座都附有開關,要讓電腦能夠跑得起來時,需要至少兩個電源插孔,為此,我刻意另外購用每個插孔均帶有個別開關的延長線插座,兩套單板電腦就買了兩條。使用的螢幕必須具有 HDMI 接頭,我有兩個這種螢幕,都是廉價回收品。一個是單純的電腦專用螢幕,另一個是帶有 HDMI 接頭的小電視。長期使用後發現,不宜將兩條電源線只透過單一個開關來控制供電,因為我發現僅由單一個開關送電時,開關會跳出火花,這就表示開關的接點在每次送電時,表面都被電擊燒過一次,開關遲早都會被燒毀,不應該這樣使用電器。螢幕與單板電腦各自使用一個電源開關時,開關就不跳火花了,以後注意,必須這樣使用。

這兩套電腦都不是很正規的日用電腦, pi3 甚至於不太適合安裝視窗作業系統,滑鼠常常跟不上響應是其最大的問題。早在 2012 年就已存在的 pi3 ,已經十幾年了,性能雖不頂好,還是能做一些事情的。東西在我這裡放了許多年,現在跑起來也沒有什麼問題。隨著時日的演進,它所需要的靜態硬碟(SD),以性價比考量,目前採用 64GB 的 MicroSD 最為恰當。舊的 32GB 之 MicroSD 不用淘汰,因為這套系統要與外界進行資料傳送時,還沒有很適合的網上軟體可供採用,只宜使用 USB 來傳送,所以舊東西就還有用處,而且比較好用,因為容量不大,資料不多時,視窗作業系統連通 USB 就比較不耗時間,也才能夠及時反應出可存取的狀態。一個裝滿資料的外加硬碟,裡面可能有幾十萬甚至於幾百萬個檔案,pi3 為了掃描整個硬碟的資料夾,所耗時間將形同已經當機。這種現象可以從 pi3 後面的兩個指示燈看得出來,紅燈亮時僅表示已經供電,綠燈亮時表示系統正在執行程式。

長久以來,我在三套作業系統 Windows', MacOS, Linux 之間傳遞資料時,都已改採透過網路直接傳送與接收的方式進行,所用軟體叫做 croc ,它用起來很方便,無論多少 個 GB 的影音圖字碼都能傳送,基本作法就是透過 GitHub 做中間緩衝環境,只要你不在乎所傳資料會被盜取,那就儘管使用 croc 來傳檔案資料,USB 就都免用了。使用 croc 的細節不是我想討論的題材,怎麼安裝?怎麼用?用時有何細節必須注意?我就不在此處討論。但是,我已試過了, pi3 沒得安裝, pi4 可以安裝,使用時卻以安全問題為由而不能用,我暫時尚未試出可用結果。所以,在 ARM 使用環境內,我仍使用硬體 USB 傳送資料。

為電腦裝上作業系統,都已採用將影像檔(.img)直接展開至 MicroSD 的方式進行,在什麼網頁可以取得那一種作業系統?不是我該在這裡介紹的題材,我只能強調我為 pi3 裝的是 Ubuntu mate 20.04,而為 pi4 裝的是 Debian 。安裝過程很簡單、也很快。只有兩件事情需要對答回應,一是時區的選用,二是設定 WiFi 的網通密碼。中文輸入法是否加裝?在安裝系統初期可以不用考慮。而且,我在安裝 pi3 時發現,加裝中文輸入界面後,滑鼠又變得慢到不可理喻的程度了,而加裝這方面的軟體,裝後不能刪除,牽涉太廣,出問題時,我改採重裝系統的方式解決問題,耗時最少。

裝好了基礎作業系統,才能發展軟體,我花了不少時間為 ciForth 延伸出 abcForth 的功能,這樣我便能大肆測試整個系統的性能。本網頁在 2 January 2025 貼出之『天下為公』一文,附貼了一份很好的多重語言互助互榮,完成數學函數繪圖的成果。這裡再次借用這份程式,同時展示在三套不同之 Linux 環境完成同一工作的成果。在桌機上的 Ubuntu 20.04 是直接完成所有工作的。在 pi3 上基於 ARM 設計的 ciForth,不能再以一次操作完成所有工作,問題出在下列這一列指令中的 -c 之工作內容會出現例外訊息

$ ./l -c datagen.f > data.txt

ciForth 在將數據寫入 data.txt 資料檔案時,出現的多餘訊息全進入了檔案頂部,我暫時也不熟悉如何修正這個問題,只好改採以人工操作滑鼠的方式刪除訊息,剩下的程式就都能執行了。三個系統的成果圖與文字操作畫面的記錄同時展示於此,供作比對之用。程式內容請回頭看『天下為公』一文,展示的效果表示,我為 ARM 環境發展的 abcForth 已經完成。

我在 pi4 的 Debian 作業環境中想要對畫面進行硬拷貝擷圖時出現困難,因為系統不提供像 Ubuntu 那樣,可以直接壓下 print screen 鍵就獲得整個螢幕畫面的能力,於是加裝了 Kazam 軟體,透過它來實現要求。
想要系統能做許多事情,加裝軟體是免不了的要求,這方面需求,因人而異,但不是我該談的主題,要談,題目也很大。
要區別那一張圖代表是在那一個作業系統內完成的?請看螢幕桌面的背景畫面就能區別。




20250519 的小作文

研發電腦技術的精神總是出現在半夜兩三點

這一段時日,我一直在設法適應 ARM CPU 的發展環境,生活有點像回到五十年前剛接觸個人電腦的日子那樣,半夜三更老是還在搞電腦。

年輕時是怕家人打擾,研究無法持續,所以選在夜晚磨練技術。但那時年輕力壯,身體很好,少睡幾小時還受得了,可以造就出後來的技能。

現在不能再這樣糟蹋身體,也沒有必要,可以就在白天自由自在的搞電腦。想半夜搞電腦,只宜顛倒作業,改靠調整睡覺時間來面對。

昨晚,我改在半夜兩點起床,搞 Raspberry pi3 名片型單板電腦的軟體加裝。它是個麻雀雖小五臟俱全的小東西,前曾被英國政府採用為兒童電腦教育專用裝備,號稱生產價格只需五美金,市售價低於 35 元。目前的版本已經進展到 pi5 ,速度與容量已與慣用電腦相當,已成歐盟國家用來抗拒美國產品的利器。

因為白天家人要用網路,這種還靠 WiFi 傳輸訊息的弱小裝備,競爭不過普通電腦訊號比較強大的裝備。因此,我長期試用過後,發現它很容易當機,原因就是處理無線電信號的能力不如別人。昨晚我八點就早睡了,刻意在半夜兩點起床,開啟 pi3 ,獨佔 WiFi,進行加裝應用軟體的操作,一試之下,果真見到了效果。凡是容量上百個 MB 的應用軟體,都能加裝成功,顯示了我的判斷沒有錯。這就是搞電腦搞到半夜三更的效果,理由仍是避免被外部打擾,靜下心來耐心發展,就能成功。

這種使用 ARM cpu 設計出來的產品,都只使用免費的 Linux 公益作業系統運作,它的使用特色就是輕鬆裝好全裸系統,然後只靠每次只打一列字的方式加裝應用軟體。如下列:

$ sudo apt install [app name]

多年來的使用經驗告訴我,大約需要加裝 30 幾套應用軟體後,才能擁有一個令我滿意的使用環境。有幾個比較大型的應用軟體都需要佔用幾百個 MB 。白天裝過幾次沒有成功之後,我痛下決心,重溫年少時的心情,再度發揮夜研精神,一下功夫,就立刻得到了結果。

今晨裝好的軟體是像小畫家那樣的工具軟體 Kolourpaint ,佔用一百多個 MB ,還有好幾個都是,半夜裝成後,我才睡回鍋覺。

今晨因此而培育出、獲得了這種電腦之新的使用經驗。東西的性能不是不好,只是運轉能力較弱,耐心應對,都能發揮效果。