一百個例題 (96 ~ 100)
曾慶潭 Ching-Tang Tseng
ilikeforth@gmail.com
Hamilton, New Zealand
7 October 2024
第(96)個範例,是一個只能在 Lina64 中執行,可以將網址內原為中文格式碼的中文文字顯示出來的程式。
由於在 W10 環境中,文字視窗內中文文字的顯示方式與 Ubuntu 作業系統中者不同,所以,不要在 W10 環境中使用這個程式。
在 Win32Forth 續用機會逐漸接近尾聲的時候,為它重新開發這個主題的程式,意義不大,此處只能盡量解釋程式的精神所在。
程式後面附貼了 20241007 在 Ubuntu V20.04 啟動 Lina64 實際載入並執行這個程式後的顯示結果,讓大家體會程式的實際用處。
我每天使用電腦來做許多事情,瀏覽網路時,收集無數的訊息,影、音、圖、字通通都有。除了訊息,為了便於回查資料出處,一定要把網路的地址也同時留下來。又由於國際各國文字編碼格式的不同,如果網址中使用了非英文的文字,每當操作滑鼠進行反白、複製取得網址,再到文字檔案中去貼上,儲存這個網址時,就會出現所謂 UTF-8 編碼與 BIG-5 編碼之不同的問題,而令取得的網址出現很多以 『 % 』 號前引,後面跟著十六進制碼的東西,無法讓人直接看懂。而且,在 W10 環境擷取這種網址存進檔案,再轉往 Linux 環境打開檔案來看時,就只能顯示這類編碼,想看正確的結果,都得經過字型編碼的規格轉換。當然,系統中有可以自動把整個檔案內容翻譯出來的指令可用,翻譯完就不能再回頭給原作業系統使用了。我很少這樣做,每次,也只有單一個網址的需求問題。
這樣擷取的網址資料,經過 20 幾年之後,留下來的有用網址,已經是成千上萬個了。晚期的網址,才會有這樣的問題,早期的,都是只有英文。現在,每當想要回查網址訊息時,就常常會遭遇到因尾綴 UTF-8 字型編碼,而無法直接看出網頁實際名稱的狀況。
我用 Forth 寫程式,處理所有我會遇到的問題,上述狀況,也是在改用 64 位元之 Linux OS 環境後,才出現的困擾,所以就設計了這個程式。實際上,在 32 位元的 Win32Forth 系統中,也能設計得出具有同樣功能的程式,程式的難度,只屬於二級的程度而已。處理關鍵,只在設法進行刪除所有的 % 符號,轉換出來的結果,在純文字視窗上,就能顯示出原來的中文字了。 % 符號,等於只是一個前引的格式碼,沒有其他的意義。
關於字串處理方面的程式,我在百例中的介紹並不很多,原因就是它們都不屬於數學計算的範疇。我在設計應用程式時,仍然常需面對字串處裡問題。可是, Forth 標準指令中,並沒有許多現成的字串處理指令可用,例如字串的存取、裁剪、比較、增刪 ..... 等等,都沒有現成指令可用,必須自己設計。我並不喜歡討論這方面的技術,因為換個系統之後,一切都得重來,為了適應新環境,重新設計程式都很耗時間,除非是為了非做不可的事情,我才會去仔細研究。
我在 20181202 貼出過關於字串處裡的基礎教學文章:字串。隨後,國際論壇上大概也因此而大肆討論了許久,最後,好像就如我所說的,許多指令雖很有用,但公認不必列為標準,會用的人,兩三行就能自行設計得出來,不熟 Forth 者仍然不會。自此以後,也不再有熱心的人士,願意像我這樣,大耗篇幅寫例題程式了。
由於在 W10 環境中,文字視窗內中文文字的顯示方式與 Ubuntu 作業系統中者不同,所以,不要在 W10 環境中使用這個程式。
在 Win32Forth 續用機會逐漸接近尾聲的時候,為它重新開發這個主題的程式,意義不大,此處只能盡量解釋程式的精神所在。
程式後面附貼了 20241007 在 Ubuntu V20.04 啟動 Lina64 實際載入並執行這個程式後的顯示結果,讓大家體會程式的實際用處。
我每天使用電腦來做許多事情,瀏覽網路時,收集無數的訊息,影、音、圖、字通通都有。除了訊息,為了便於回查資料出處,一定要把網路的地址也同時留下來。又由於國際各國文字編碼格式的不同,如果網址中使用了非英文的文字,每當操作滑鼠進行反白、複製取得網址,再到文字檔案中去貼上,儲存這個網址時,就會出現所謂 UTF-8 編碼與 BIG-5 編碼之不同的問題,而令取得的網址出現很多以 『 % 』 號前引,後面跟著十六進制碼的東西,無法讓人直接看懂。而且,在 W10 環境擷取這種網址存進檔案,再轉往 Linux 環境打開檔案來看時,就只能顯示這類編碼,想看正確的結果,都得經過字型編碼的規格轉換。當然,系統中有可以自動把整個檔案內容翻譯出來的指令可用,翻譯完就不能再回頭給原作業系統使用了。我很少這樣做,每次,也只有單一個網址的需求問題。
這樣擷取的網址資料,經過 20 幾年之後,留下來的有用網址,已經是成千上萬個了。晚期的網址,才會有這樣的問題,早期的,都是只有英文。現在,每當想要回查網址訊息時,就常常會遭遇到因尾綴 UTF-8 字型編碼,而無法直接看出網頁實際名稱的狀況。
我用 Forth 寫程式,處理所有我會遇到的問題,上述狀況,也是在改用 64 位元之 Linux OS 環境後,才出現的困擾,所以就設計了這個程式。實際上,在 32 位元的 Win32Forth 系統中,也能設計得出具有同樣功能的程式,程式的難度,只屬於二級的程度而已。處理關鍵,只在設法進行刪除所有的 % 符號,轉換出來的結果,在純文字視窗上,就能顯示出原來的中文字了。 % 符號,等於只是一個前引的格式碼,沒有其他的意義。
關於字串處理方面的程式,我在百例中的介紹並不很多,原因就是它們都不屬於數學計算的範疇。我在設計應用程式時,仍然常需面對字串處裡問題。可是, Forth 標準指令中,並沒有許多現成的字串處理指令可用,例如字串的存取、裁剪、比較、增刪 ..... 等等,都沒有現成指令可用,必須自己設計。我並不喜歡討論這方面的技術,因為換個系統之後,一切都得重來,為了適應新環境,重新設計程式都很耗時間,除非是為了非做不可的事情,我才會去仔細研究。
我在 20181202 貼出過關於字串處裡的基礎教學文章:字串。隨後,國際論壇上大概也因此而大肆討論了許久,最後,好像就如我所說的,許多指令雖很有用,但公認不必列為標準,會用的人,兩三行就能自行設計得出來,不熟 Forth 者仍然不會。自此以後,也不再有熱心的人士,願意像我這樣,大耗篇幅寫例題程式了。
\ (96) url translator 20180710 0 value ptr create url 32 cells allot create web 32 cells allot : reset$buf url 32 cells erase web 32 cells erase cr ." Input url string $ " cr input$ url $! ; : setptr url $@ drop to ptr ; : url>web begin ptr c@ 0 <> while ptr c@ [char] % = if HEX 0. ptr 1+ 2 >number 2drop d>s web $c+ DECIMAL 3 +to ptr else ptr c@ web $c+ 1 +to ptr then repeat ; : show cr ." url = " url $@ type cr cr ." web = " web $@ type cr ; : main reset$buf setptr url>web show ; main \s ching@ctt:~$ ./l AMDX86 ciforth 5.3.0 fload 96.f Input url string $ http://www.teepr.com/376386/lexychang/%E4%B8%B9%E9%BA%A5%E5%A4%A9%E4%BD%BF%E7%9A%84%E7%B4%A0%E9%A1%8F%E7%85%A7%E6%9C%83%E8%AE%93%E4%BD%A0%E7%9F%A5%E9%81%93%E4%BB%80%E9%BA%BC%E6%98%AF%E3%80%8C%E7%9C%9F%E6%AD%A3%E7%9A%84%E4%BB%99%E5%A5%B3/ url = http://www.teepr.com/376386/lexychang/%E4%B8%B9%E9%BA%A5%E5%A4%A9%E4%BD%BF%E7%9A%84%E7%B4%A0%E9%A1%8F%E7%85%A7%E6%9C%83%E8%AE%93%E4%BD%A0%E7%9F%A5%E9%81%93%E4%BB%80%E9%BA%BC%E6%98%AF%E3%80%8C%E7%9C%9F%E6%AD%A3%E7%9A%84%E4%BB%99%E5%A5%B3/ web = http://www.teepr.com/376386/lexychang/丹麥天使的素顏照會讓你知道什麼是「真正的仙女/ OK
第(97)個範例程式是用來測試 Lina64 系統中的矩陣及亂數函數應用的結果,兩項成果顯示都有很好的性能。
關於這兩大項的測試,我分開來解釋,並附上實際執行後的測試輸出結果,便於說明。
關於矩陣的測試,只偏重於測出帶 0 指標的單元,是否已被正常的納入系統?用不用都無所謂,輸出時就必須顯示它們的存在。這一特點,在原本的 Win32Forth ABC Forth 系統中沒有刻意安排。我是在使用許多大型數學計算套裝軟體後,才體會出必須令其存在的,否則會有很多現成的程式,在抄用時會遭遇很大的困擾,必須人工自行調整指標來對應,不如就在建系統時加入此一功能,會方便許多。當然,每次宣告陣列、矩陣來用時,就會損失一些可能用不上的記憶體。今日世界,這已不再是問題,那就不要省,對我而言,加設計也只是舉手之勞。這是這個範例測試出設計成功的一個項目。宣告後,沒用到的具有 0 指標之單元,內容全為 0 ,這就是成功的結果。
其次,關於亂數,分成三大項來看結果,我設計系統時沒有前人的文獻可以參考,自己設計後,使用上的『協定』也只好自己規定了。
所有的亂數都是炒作整數亂數後延生出來的,炒作方法就是把位元花樣向左、向右移動幾個質數次,再與原花樣進行 XOR 運算,保證與原花樣完全不一樣,這樣搞個幾次,就成了新的亂數,下一個照樣畫葫蘆。起始花樣,則藉 CPU 旁邊會跑個不停的計時器內容之下半段位元花樣來亂化,使用這種軟、硬通吃的技術,效果就會很好。
這樣產生的整數亂數,因用到全部的位元來生亂,最高位元當然也被炒到了,程式才容易設計,所以,直接使用時,會有負值出現,加一個組合語言指令 ABS 就能解決問題,亂數產生的速度非常快,是 giga 分之幾秒就能完成。但我認為負的亂數也很有用,功能就保留下來了,用時自己注意。
我的浮點數是全用整數四則運算完成的十進制浮點,拿整數亂數直接用,當然就更方便。只是用時要注意,一般的基本用法,都是先取得只介於 0 至 1 之間的浮點亂數,想放大多少,自己乘上去後才使用,就得了。
我的複數系統是一般數學運算套裝軟體系統上不太具有的東西,所以就不用找規格了,使用『協定』也只好自己創立,這個範例中有簡單的使用規格,叫用亂數函數時,使用者提供的參數就是新產生之亂數的上限範圍,實數的部分可以與虛數的部分不一樣,例如此範例中,實數上限是 100 ,虛數上限是 10 ,測試結果,顯示都很好,非常恰當。
關於這兩大項的測試,我分開來解釋,並附上實際執行後的測試輸出結果,便於說明。
關於矩陣的測試,只偏重於測出帶 0 指標的單元,是否已被正常的納入系統?用不用都無所謂,輸出時就必須顯示它們的存在。這一特點,在原本的 Win32Forth ABC Forth 系統中沒有刻意安排。我是在使用許多大型數學計算套裝軟體後,才體會出必須令其存在的,否則會有很多現成的程式,在抄用時會遭遇很大的困擾,必須人工自行調整指標來對應,不如就在建系統時加入此一功能,會方便許多。當然,每次宣告陣列、矩陣來用時,就會損失一些可能用不上的記憶體。今日世界,這已不再是問題,那就不要省,對我而言,加設計也只是舉手之勞。這是這個範例測試出設計成功的一個項目。宣告後,沒用到的具有 0 指標之單元,內容全為 0 ,這就是成功的結果。
其次,關於亂數,分成三大項來看結果,我設計系統時沒有前人的文獻可以參考,自己設計後,使用上的『協定』也只好自己規定了。
所有的亂數都是炒作整數亂數後延生出來的,炒作方法就是把位元花樣向左、向右移動幾個質數次,再與原花樣進行 XOR 運算,保證與原花樣完全不一樣,這樣搞個幾次,就成了新的亂數,下一個照樣畫葫蘆。起始花樣,則藉 CPU 旁邊會跑個不停的計時器內容之下半段位元花樣來亂化,使用這種軟、硬通吃的技術,效果就會很好。
這樣產生的整數亂數,因用到全部的位元來生亂,最高位元當然也被炒到了,程式才容易設計,所以,直接使用時,會有負值出現,加一個組合語言指令 ABS 就能解決問題,亂數產生的速度非常快,是 giga 分之幾秒就能完成。但我認為負的亂數也很有用,功能就保留下來了,用時自己注意。
我的浮點數是全用整數四則運算完成的十進制浮點,拿整數亂數直接用,當然就更方便。只是用時要注意,一般的基本用法,都是先取得只介於 0 至 1 之間的浮點亂數,想放大多少,自己乘上去後才使用,就得了。
我的複數系統是一般數學運算套裝軟體系統上不太具有的東西,所以就不用找規格了,使用『協定』也只好自己創立,這個範例中有簡單的使用規格,叫用亂數函數時,使用者提供的參數就是新產生之亂數的上限範圍,實數的部分可以與虛數的部分不一樣,例如此範例中,實數上限是 100 ,虛數上限是 10 ,測試結果,顯示都很好,非常恰當。
\ (97 random number test \ beware! In integer mode, need a NEGATE operation to get negative random number : NTEST 10 0 DO CR 10 0 DO 100 RAND negate 4 .R LOOP LOOP ; : FTEST 10 0 DO CR -1.0 E 2 FRAND F. LOOP ; \ beware! upper bound input format ignore + and ii : ZTEST 10 0 DO CR -1.0 E 2 1.0 E 1 ZRAND Z. LOOP ; 3 integers i j k complex z0 3 3 3 [tensor] zzz : init basic 10 let z{ z0 = 0.0 E 0 + 0.0 E 0 ii }z 20 for i = 1 to 3 30 for j = 1 to 3 40 for k = 1 to 3 \ attention! this is the usage to create a complex random number 50 let z{ zzz ( i j k ) = RND ( 1.0 E 2 - 1.0 E 1 ii ) }z 60 next k 70 next j 80 next i 90 end ; : MAIN randomize ntest cr ftest cr ztest cr init basic 10 for i = 0 to 3 20 for j = 0 to 3 30 for k = 0 to 3 40 print " zzz( " ; i ; j ; k ; " )= " ; z{ zzz ( i j k ) }z 50 let z{ z0 = z0 + zzz ( i j k ) }z 60 next k 70 next j 80 next i 90 print cr cr " z0 = " ; z{ z0 }z 100 end cr ." --- The end of testing --- " ; \s ching@center:~$ cd lina64 ching@center:~/lina64$ ./f5124 AMDX86 ciforth 5.3.0 fload 97 I : ISN'T UNIQUE J : ISN'T UNIQUE OK main -10 -8 -72 -61 -31 -86 -14 -7 -86 -20 -42 -94 -92 -27 -86 -45 -57 -77 -34 -4 -98 -89 -61 -28 -86 -66 -36 -71 -68 -71 -35 -10 -10 -84 -85 -82 -23 -14 -89 -20 -34 -51 -10 -87 -58 -67 -21 -64 -43 -51 -26 -18 -63 -99 -30 -22 -34 -92 -25 -78 -58 -58 -98 -64 -93 -54 -23 -43 -99 -38 -83 -68 -50 -68 -11 -74 -44 -49 -85 -42 -52 -18 -23 -93 -39 -41 -37 -16 -49 -67 -40 -39 -66 -99 -70 -45 -99 -16 -17 -28 -1.913628913 -89.96769746 -90.80277685 -93.25782136 -12.5438047 -68.49962742 -8.071056155 -61.09243441 -47.21671679 -32.33685587 -3.989974468+ 4.9740950527ii -77.58595045+ 0.7664840648ii -42.15458337+ 0.0229446231ii -5.083676585+ 6.5485581007ii -62.74442857+ 5.2724854219ii -70.1131553+ 6.2933488809ii -37.53475328+ 6.8368182786ii -38.22238984+ 1.2874766663ii -96.85347849+ 4.7545419669ii -3.281314505+ 5.6388045221ii zzz( 0 0 0 )= 0 zzz( 0 0 1 )= 0 zzz( 0 0 2 )= 0 zzz( 0 0 3 )= 0 zzz( 0 1 0 )= 0 zzz( 0 1 1 )= 0 zzz( 0 1 2 )= 0 zzz( 0 1 3 )= 0 zzz( 0 2 0 )= 0 zzz( 0 2 1 )= 0 zzz( 0 2 2 )= 0 zzz( 0 2 3 )= 0 zzz( 0 3 0 )= 0 zzz( 0 3 1 )= 0 zzz( 0 3 2 )= 0 zzz( 0 3 3 )= 0 zzz( 1 0 0 )= 0 zzz( 1 0 1 )= 0 zzz( 1 0 2 )= 0 zzz( 1 0 3 )= 0 zzz( 1 1 0 )= 0 zzz( 1 1 1 )= 16.138346694- 1.6922562207ii zzz( 1 1 2 )= 11.673369547- 0.0886917048ii zzz( 1 1 3 )= 12.744789187- 3.7724680765ii zzz( 1 2 0 )= 0 zzz( 1 2 1 )= 31.962691299- 1.3816571182ii zzz( 1 2 2 )= 40.44457283- 1.5831628868ii zzz( 1 2 3 )= 20.592126444- 4.1070317085ii zzz( 1 3 0 )= 0 zzz( 1 3 1 )= 5.7211277157- 1.9009267784ii zzz( 1 3 2 )= 79.010045666- 1.7076510055ii zzz( 1 3 3 )= 36.175071964- 1.1219479774ii zzz( 2 0 0 )= 0 zzz( 2 0 1 )= 0 zzz( 2 0 2 )= 0 zzz( 2 0 3 )= 0 zzz( 2 1 0 )= 0 zzz( 2 1 1 )= 15.89740986- 2.4554373865ii zzz( 2 1 2 )= 82.495140485- 8.5362387692ii zzz( 2 1 3 )= 17.291546835- 8.3884016077ii zzz( 2 2 0 )= 0 zzz( 2 2 1 )= 15.663485123- 8.9183735609ii zzz( 2 2 2 )= 87.857473069- 2.1572277506ii zzz( 2 2 3 )= 8.0974462118- 4.8481376385ii zzz( 2 3 0 )= 0 zzz( 2 3 1 )= 16.89172751- 5.8226671362ii zzz( 2 3 2 )= 67.783749813- 1.849634518ii zzz( 2 3 3 )= 57.98394664- 6.3832837116ii zzz( 3 0 0 )= 0 zzz( 3 0 1 )= 0 zzz( 3 0 2 )= 0 zzz( 3 0 3 )= 0 zzz( 3 1 0 )= 0 zzz( 3 1 1 )= 5.9138480523- 6.632469444ii zzz( 3 1 2 )= 92.038071469- 2.2556417577ii zzz( 3 1 3 )= 18.729357865- 7.4502652215ii zzz( 3 2 0 )= 0 zzz( 3 2 1 )= 67.299863245- 2.0522561644ii zzz( 3 2 2 )= 80.622464446- 2.3218369173ii zzz( 3 2 3 )= 74.024361093- 3.2010649041ii zzz( 3 3 0 )= 0 zzz( 3 3 1 )= 95.399681736- 5.8407040896ii zzz( 3 3 2 )= 59.354835252- 6.4971174443ii zzz( 3 3 3 )= 12.681339537- 9.1802079012ii z0 = 1130.4878896- 112.1467594ii --- The end of testing --- OK
第(98)個範例程式是用來解決一個根據巴斯卡(Pascal)三角塔原理為基礎所演變出來的問題,讓程式可以進行矩陣運算,也讓結果易於被核驗。
高階二項式 (a+b)**n 的展開式,每一項的係數所形成的固定變化,曾被一位法國的數學家叫布萊斯‧巴斯卡(Blaise Pascal,1623年6月19日-1662年8月19日),妥善整理出來,是一個漂亮的三角塔式的係數數列,故得其名。但實際上,對這個問題有過研究的古老文獻,非常的多,顯然,發現人並不是巴斯卡,甚至於中國人也有,這方面,就不用去考究了,我們直稱其為巴斯卡三角塔便可。
巴斯卡三角塔並不容易被電腦程式漂亮的印出來,為了方便,只適合把三角塔印成上三角矩陣,或者是下三角矩陣。因此,這個程式的題目,就要求計算出把這兩種矩陣加起來後的結果到底為何?
我控留此程式的目的,主要是因為這樣的題目能磨練程式的寫法,另外,也是個能夠用來驗證矩陣的資料結構設計得是否正確之題目。算是一個優良的測試材料。關於磨練程式設計技術的部份,請自行看看程式本身,就能了解。關於驗證資料結構是否正確的問題,則還能進一步探討。
巴斯卡三角塔的整齊結構,易於只用肉眼觀察,就能看出對錯。兩個矩陣相加後,因為元素內容的花樣,係與對角線對稱,也易於只用肉眼觀察,就能看出對錯。一般而言,我們使用的電腦,宣告出 20X20 的矩陣,螢幕上尚能印出不需跳列的輸出,能測試到這樣的尺度,大約也足夠表示設計無誤了。
所謂的進一步探討,並不絕對需要,但對矩陣運算系統的設計與發展而言,就很有必要。大家可以看看程式的寫法,您能發現,就算要把矩陣印出來,也得在被印出程式之指令外面,先行宣告出矩陣的維度,才辦得到。後續那個矩陣的加法運算,亦然。這就表示,寫成的這套程式,代表著所有的矩陣運算程式都得這樣處理。
我在發展出許多數學計算系統後,自覺能夠改善矩陣體系運算系統的性能,能夠設計得出 M+ , M- , M* , M/ , Mx , M. , 1/M , Mt , M0 , Mneg ..... 等等,可以不管宣告維度是多少,就能直接使用的運算指令。這樣的性能,必須依靠矩陣在宣告產生時,就把維度也存在資料結構中才行,我辦到了這項設計,但沒有進行矩陣運算系統的發展,因我個人很少有接觸這方面問題的機會。
事實上,我還有許多的構想,可以設計出更多的數學運算體系,都很有意義。但自從進入 64 位元的環境後,面對的系統發展問題,不是更多的數學體系,而是更多的不同 Forth 基本平台。因此,發現研究的主要方向,自己必須嚴格把握住,才有發展的坦途可走。整個系統的基礎,若不專心設計得更好,發展再多的數學體系,只會弄亂自己的陣腳。於是,停止了矩陣體系運算系統的開發了。
Win32Forth 系統,可以順利的執行出這個程式。純粹只處理整數的數學問題,這類程式都能這樣被直接執行得出來。
高階二項式 (a+b)**n 的展開式,每一項的係數所形成的固定變化,曾被一位法國的數學家叫布萊斯‧巴斯卡(Blaise Pascal,1623年6月19日-1662年8月19日),妥善整理出來,是一個漂亮的三角塔式的係數數列,故得其名。但實際上,對這個問題有過研究的古老文獻,非常的多,顯然,發現人並不是巴斯卡,甚至於中國人也有,這方面,就不用去考究了,我們直稱其為巴斯卡三角塔便可。
巴斯卡三角塔並不容易被電腦程式漂亮的印出來,為了方便,只適合把三角塔印成上三角矩陣,或者是下三角矩陣。因此,這個程式的題目,就要求計算出把這兩種矩陣加起來後的結果到底為何?
我控留此程式的目的,主要是因為這樣的題目能磨練程式的寫法,另外,也是個能夠用來驗證矩陣的資料結構設計得是否正確之題目。算是一個優良的測試材料。關於磨練程式設計技術的部份,請自行看看程式本身,就能了解。關於驗證資料結構是否正確的問題,則還能進一步探討。
巴斯卡三角塔的整齊結構,易於只用肉眼觀察,就能看出對錯。兩個矩陣相加後,因為元素內容的花樣,係與對角線對稱,也易於只用肉眼觀察,就能看出對錯。一般而言,我們使用的電腦,宣告出 20X20 的矩陣,螢幕上尚能印出不需跳列的輸出,能測試到這樣的尺度,大約也足夠表示設計無誤了。
所謂的進一步探討,並不絕對需要,但對矩陣運算系統的設計與發展而言,就很有必要。大家可以看看程式的寫法,您能發現,就算要把矩陣印出來,也得在被印出程式之指令外面,先行宣告出矩陣的維度,才辦得到。後續那個矩陣的加法運算,亦然。這就表示,寫成的這套程式,代表著所有的矩陣運算程式都得這樣處理。
我在發展出許多數學計算系統後,自覺能夠改善矩陣體系運算系統的性能,能夠設計得出 M+ , M- , M* , M/ , Mx , M. , 1/M , Mt , M0 , Mneg ..... 等等,可以不管宣告維度是多少,就能直接使用的運算指令。這樣的性能,必須依靠矩陣在宣告產生時,就把維度也存在資料結構中才行,我辦到了這項設計,但沒有進行矩陣運算系統的發展,因我個人很少有接觸這方面問題的機會。
事實上,我還有許多的構想,可以設計出更多的數學運算體系,都很有意義。但自從進入 64 位元的環境後,面對的系統發展問題,不是更多的數學體系,而是更多的不同 Forth 基本平台。因此,發現研究的主要方向,自己必須嚴格把握住,才有發展的坦途可走。整個系統的基礎,若不專心設計得更好,發展再多的數學體系,只會弄亂自己的陣腳。於是,停止了矩陣體系運算系統的開發了。
Win32Forth 系統,可以順利的執行出這個程式。純粹只處理整數的數學問題,這類程式都能這樣被直接執行得出來。
\ (98) Pascal matrix generator 4 integers i j k n 20 20 (matrix) mm : i-1 i 1- ; : j-1 j 1- ; : printmm basic 100 for i = 1 to n 110 for j = 1 to n 120 let k = mm ( i j ) 130 run k 8 .r space 140 next j 150 run cr 160 next i 200 end ; : upm basic 10 for j = 1 to n 20 let mm ( 1 j ) = 1 30 next j 40 for i = 2 to n 50 let mm ( i 1 ) = 0 60 for j = 2 to n 70 let mm ( i j ) = mm ( i-1 j-1 ) + mm ( i j-1 ) 80 next j 90 next i 500 end ; : lpm basic 10 for i = 1 to n 20 let mm ( i 1 ) = 1 30 next i 40 for j = 2 to n 50 let mm ( 1 j ) = 0 60 for i = 2 to n 70 let mm ( i j ) = mm ( i-1 j-1 ) + mm ( i-1 j ) 80 next i 90 next j 500 end ; : spm basic 10 for i = 1 to n 20 let mm ( i 1 ) = 1 :: mm ( 1 i ) = 1 30 next i 40 for j = 2 to n 50 for i = 2 to n 60 let mm ( i j ) = mm ( i j-1 ) + mm ( i-1 j ) 70 next i 80 next j 90 end ; : pascal ( n -- ) [[ n ]] ! basic 10 run cr upm 20 run cr printmm 30 run cr lpm 40 run cr printmm 50 run cr spm 60 run cr printmm 70 end ; page 5 pascal
第(99)個範例是在『Euler數學問題網頁』中,條列之一個問題的電腦程式解法。
問題問:直角三角形的邊長為 1000 ,請問三邊邊長均為整數時的長度各為多少?
Euler’s 數學問題網頁,是一個最近兩年才聞名起來的網頁,所出問題,大部分是需要能夠運算大數目字的系統才能解答的題目,現已大約收集了六百多個問題,貼出在網上,讓人隨意發揮。剛開始時,網頁出入管制,沒有那麼嚴格,我有空時,就會去拿題目,試一試自己的系統,那時,大約我都能解,解題時,只覺得必須全靠電腦,很難有單憑思考就能解析出來的題目。後來,該網頁開始進行管制了,必須登記身份才能使用,我就放棄了追蹤。
話雖如此,那種數學題目,反而代表了現今網路環境熱門話題的演變趨勢,都屬於同一類型的問題。例如,要您從海量資料中,比對出合乎指定條件的網上訊息,您就得像處理無限位數的大數那樣,把一小節數字用電腦找出來。所以,這個數學問題網頁的建立,是一個時代的產物。以後,如果您有興趣也有本領解這種題目,就可以根據名稱,自行前往該網頁進行探索。我不參與,所以也沒留網址,但很容易搜索得到。
我留下這個程式的目的,除了題目優良外,還有個簡單意義,就是畢氏定理的運用,能讓我寫成的程式,比別種程式語言的語法更為清楚、簡單。本來,問題的表面只有ㄧ個條件,就是 a+b+c=1000 ,其中,有兩個獨立變數,一個因變數,條件只有一個時,沒辦法有特解,只能有也附帶條件式的通解。如果學過了數學上的畢氏定理,您就知道另有一個很好的輔助條件可以利用,也就是斜邊的平方必須等於另兩個直角邊之各自平方的和,亦即 a**2+b**2=c**2 。
這麼一來,寫程式時,語法好的系統,就很容易讓人看懂,執行起來,就不容易出差錯, BASIC 程式語言的語法,就具有這種優點。仔細看這不到十列的程式,使用兩個環路、一個邏輯判斷來完成,兩個環路就是給兩個獨立變數用的,範圍就指定在 1 到 1000 之間。邏輯判斷就只給畢氏定理使用,以取得因變數之值,合條件者就是答案,不合條件者就不是答案。這樣的電腦解法,讓普通人純用手算,就會成為不切實際的解題方法,用電腦算,又快又準,立刻就能得到答案。此程式,只處理整數,就可以在我設計的系統中執行得出來,我就不在此處提供解答,印出來的答案一定對,您可以自行核算。
本來, Forth 系統的本質,就是一種具有延伸性的程式語言,我沒附加 BASIC 式語法的執行能力前,早就有名家為 Win32Forth 系統添加了組合語言的功能,只是後來大家都不太使用了,所以罕有人再強調。但是,這套 Win32Forth 中的 Assembler 非常有名,許多專門介紹組合語言的網頁中,竟然也把這套組合語言列為一套正式的特產,性能既好也完整。
事實上,我自創添加進去的 BASIC 式語法,等同於 Forth 系統中的 Assembler ,就是 Forth 中的 BASIC 。
一旦一套 Forth 系統,同時具有以他種語言之語法執行出結果的功能時,這個 Forth 系統,就也具有能夠在語法中互相來去、交流穿插的功能。這也就是為什麼我能利用這種精神,在 BASIC 語法中創建出一個 RUN 指令的原因。我把雙方語言的特質都搞清楚了,賦予這種好處,就不是難事。這樣的發揮, Forth 系統的發明人並沒有善加利用。實際上,他有點痛恨 BASIC 在當年霸佔市場的強勢狀態,那時,他好像是只為了要藐視一下 BASIC ,才發表了他的那篇 Tiny BASIC Compiler 論文。我,則是取長補短,設計出了自己的東西。
另外, Charles H. Moore 終生都痛恨浮點數運算,所以他也從來不寫浮點數運算方面的程式。這一點,我個人認為不對,所以,我自創系統中所有的浮點數計算功能,如何實現?都是我自己安排、自己實現的設計。當然,仍屬於 Forth 浮點範疇的部份,只要能師法於現成的成果,我就取用,如果沒有,我就自行設計,現在也已很熟了。
我仍然是站在巨人的肩膀上創作系統,自己的成就只有一小部份。
問題問:直角三角形的邊長為 1000 ,請問三邊邊長均為整數時的長度各為多少?
Euler’s 數學問題網頁,是一個最近兩年才聞名起來的網頁,所出問題,大部分是需要能夠運算大數目字的系統才能解答的題目,現已大約收集了六百多個問題,貼出在網上,讓人隨意發揮。剛開始時,網頁出入管制,沒有那麼嚴格,我有空時,就會去拿題目,試一試自己的系統,那時,大約我都能解,解題時,只覺得必須全靠電腦,很難有單憑思考就能解析出來的題目。後來,該網頁開始進行管制了,必須登記身份才能使用,我就放棄了追蹤。
話雖如此,那種數學題目,反而代表了現今網路環境熱門話題的演變趨勢,都屬於同一類型的問題。例如,要您從海量資料中,比對出合乎指定條件的網上訊息,您就得像處理無限位數的大數那樣,把一小節數字用電腦找出來。所以,這個數學問題網頁的建立,是一個時代的產物。以後,如果您有興趣也有本領解這種題目,就可以根據名稱,自行前往該網頁進行探索。我不參與,所以也沒留網址,但很容易搜索得到。
我留下這個程式的目的,除了題目優良外,還有個簡單意義,就是畢氏定理的運用,能讓我寫成的程式,比別種程式語言的語法更為清楚、簡單。本來,問題的表面只有ㄧ個條件,就是 a+b+c=1000 ,其中,有兩個獨立變數,一個因變數,條件只有一個時,沒辦法有特解,只能有也附帶條件式的通解。如果學過了數學上的畢氏定理,您就知道另有一個很好的輔助條件可以利用,也就是斜邊的平方必須等於另兩個直角邊之各自平方的和,亦即 a**2+b**2=c**2 。
這麼一來,寫程式時,語法好的系統,就很容易讓人看懂,執行起來,就不容易出差錯, BASIC 程式語言的語法,就具有這種優點。仔細看這不到十列的程式,使用兩個環路、一個邏輯判斷來完成,兩個環路就是給兩個獨立變數用的,範圍就指定在 1 到 1000 之間。邏輯判斷就只給畢氏定理使用,以取得因變數之值,合條件者就是答案,不合條件者就不是答案。這樣的電腦解法,讓普通人純用手算,就會成為不切實際的解題方法,用電腦算,又快又準,立刻就能得到答案。此程式,只處理整數,就可以在我設計的系統中執行得出來,我就不在此處提供解答,印出來的答案一定對,您可以自行核算。
本來, Forth 系統的本質,就是一種具有延伸性的程式語言,我沒附加 BASIC 式語法的執行能力前,早就有名家為 Win32Forth 系統添加了組合語言的功能,只是後來大家都不太使用了,所以罕有人再強調。但是,這套 Win32Forth 中的 Assembler 非常有名,許多專門介紹組合語言的網頁中,竟然也把這套組合語言列為一套正式的特產,性能既好也完整。
事實上,我自創添加進去的 BASIC 式語法,等同於 Forth 系統中的 Assembler ,就是 Forth 中的 BASIC 。
一旦一套 Forth 系統,同時具有以他種語言之語法執行出結果的功能時,這個 Forth 系統,就也具有能夠在語法中互相來去、交流穿插的功能。這也就是為什麼我能利用這種精神,在 BASIC 語法中創建出一個 RUN 指令的原因。我把雙方語言的特質都搞清楚了,賦予這種好處,就不是難事。這樣的發揮, Forth 系統的發明人並沒有善加利用。實際上,他有點痛恨 BASIC 在當年霸佔市場的強勢狀態,那時,他好像是只為了要藐視一下 BASIC ,才發表了他的那篇 Tiny BASIC Compiler 論文。我,則是取長補短,設計出了自己的東西。
另外, Charles H. Moore 終生都痛恨浮點數運算,所以他也從來不寫浮點數運算方面的程式。這一點,我個人認為不對,所以,我自創系統中所有的浮點數計算功能,如何實現?都是我自己安排、自己實現的設計。當然,仍屬於 Forth 浮點範疇的部份,只要能師法於現成的成果,我就取用,如果沒有,我就自行設計,現在也已很熟了。
我仍然是站在巨人的肩膀上創作系統,自己的成就只有一小部份。
(99)euler9 \ Subject: Project Euler Problem 9 : unique Pythagorean triple summing to 1000 \ a+b+c=1000 3 integers a b c : main basic 10 for a = 1 to 1000 20 for b = a to 1000 30 let c = 1000 - a - b 40 if ( a * a + b * b ) = ( c * c ) then 60 50 goto 70 60 print a , b , c 70 next b 80 next a 90 end ;
第(100)個範例,是一個能夠操作出讓單一個位元為 1 或為 0 的工具程式。
整台電腦中任一位址的內容,可以被取來放入 bitbuffer 變數內,然後才用這個程式中的操作指令,來令其中的單一個位元成為 1 或成為 0 。
操作的方法,在程式中有舉例,也只列出了一個顯示結果,要怎樣用才會更為方便?請自己加添設計出更簡單的指令,就能隨您所願。原本這個程式是在 64 位元環境中由 Bob Dickow 設計的,我試用後覺得比我自己設計的更好用,程式略作修改後,就被收集進這個範例了。
這種『位元操作』能力的展現,是一種 Forth 程式語言之特殊性能,別種程式語言都很難辦到這麼方便的設計格式,我用了 40 多年的 Forth ,只要想到要進行一些位元操作的工作時,深知非 Forth 莫屬。
我設計過一份以 Wina32 為平台,全由叫用硬體 co-processor 方式來完成所有浮點計算工作的系統。那個系統是標準 32 位元的設計,數據在 CPU 與 co-processor 間傳遞,執行所有的軟體指令都沒問題,硬體接線也與數據傳輸要求規格相符,所以,我很快便完成了設計,實際使用時,也很方便,很快就完成了 ABC Forth 的增建。
後來,我轉往 64 位元以 Lina64 為平台重新發展系統時,首先遭遇到的問題,就是從 CPU 內傳送 64 位元的單整數當 32 元的雙整數到 co-processor 後,因為硬體接線的格式與 32 位元者完全不同,我就不能再用 co-processor 設計浮點系統了。這問題不單純只是軟體指令的不能使用而已,硬體接線與數據傳輸時脈規格也完全不同,所以不能再寫組合語言實現同樣的夢想。除非能令系統回到 32 位元執行浮點運算,算完,再回到 64 位元系統顯示結果,這很麻煩,叫用 C 的庫存程式可以完成,但 gForth 系統就顯示了執行速度非常慢的問題,不值得這樣做。
我為什麼知道上述問題?就是因爲我使用了 Forth 的位元操作能力,仔細的測試過問題。把簡單位元花樣的數字,在 CPU 與 co-processor 間來回傳輸、簡單執行,再印出前後結果,一經比對,發現連一個簡單位元花樣的 1.2E0 浮點數,都沒辦法處裡得正確。無論以軟體設計了多少個委屈程式,都沒辦法獲得正確的結果。最後,只好放棄使用硬體,改成走自創純軟體設計之十進制浮點系統的發展方向,現已完成了設計。
這百例中,有許多範例都是我在發展系統期間所使用的工具程式,都有著深刻的印象與使用經驗,這些範例都不是幾天之內就能完成的研究,它們能用時,都是經過長久研發與各種測試步驟後,才得到的結果。
我貼完了百例,也對自己過去所做過的工作,進行了一次完整的複習,對我自己而言,很有益處。
整台電腦中任一位址的內容,可以被取來放入 bitbuffer 變數內,然後才用這個程式中的操作指令,來令其中的單一個位元成為 1 或成為 0 。
操作的方法,在程式中有舉例,也只列出了一個顯示結果,要怎樣用才會更為方便?請自己加添設計出更簡單的指令,就能隨您所願。原本這個程式是在 64 位元環境中由 Bob Dickow 設計的,我試用後覺得比我自己設計的更好用,程式略作修改後,就被收集進這個範例了。
這種『位元操作』能力的展現,是一種 Forth 程式語言之特殊性能,別種程式語言都很難辦到這麼方便的設計格式,我用了 40 多年的 Forth ,只要想到要進行一些位元操作的工作時,深知非 Forth 莫屬。
我設計過一份以 Wina32 為平台,全由叫用硬體 co-processor 方式來完成所有浮點計算工作的系統。那個系統是標準 32 位元的設計,數據在 CPU 與 co-processor 間傳遞,執行所有的軟體指令都沒問題,硬體接線也與數據傳輸要求規格相符,所以,我很快便完成了設計,實際使用時,也很方便,很快就完成了 ABC Forth 的增建。
後來,我轉往 64 位元以 Lina64 為平台重新發展系統時,首先遭遇到的問題,就是從 CPU 內傳送 64 位元的單整數當 32 元的雙整數到 co-processor 後,因為硬體接線的格式與 32 位元者完全不同,我就不能再用 co-processor 設計浮點系統了。這問題不單純只是軟體指令的不能使用而已,硬體接線與數據傳輸時脈規格也完全不同,所以不能再寫組合語言實現同樣的夢想。除非能令系統回到 32 位元執行浮點運算,算完,再回到 64 位元系統顯示結果,這很麻煩,叫用 C 的庫存程式可以完成,但 gForth 系統就顯示了執行速度非常慢的問題,不值得這樣做。
我為什麼知道上述問題?就是因爲我使用了 Forth 的位元操作能力,仔細的測試過問題。把簡單位元花樣的數字,在 CPU 與 co-processor 間來回傳輸、簡單執行,再印出前後結果,一經比對,發現連一個簡單位元花樣的 1.2E0 浮點數,都沒辦法處裡得正確。無論以軟體設計了多少個委屈程式,都沒辦法獲得正確的結果。最後,只好放棄使用硬體,改成走自創純軟體設計之十進制浮點系統的發展方向,現已完成了設計。
這百例中,有許多範例都是我在發展系統期間所使用的工具程式,都有著深刻的印象與使用經驗,這些範例都不是幾天之內就能完成的研究,它們能用時,都是經過長久研發與各種測試步驟後,才得到的結果。
我貼完了百例,也對自己過去所做過的工作,進行了一次完整的複習,對我自己而言,很有益處。
\ (100)FORTH BIT Arrays , by Bob Dickow dickow@turbonet.com \ version 1.0.3 Mar 2, 2018 \ Notes: \ endian agnostic, address unit agnostic (untested) \ Vocabulary (internal dependencies) ^2MASKS bmask _BIT@ \ Vocabulary: 2^ BIT@ BIT! BOOL@ BOOL! \ BOOL@ and BOOL! are convenience words to avoid converting FALSE to 1 \ 2^ may be handy outside the array access words. Included for compatibility with earlier versions \ Buffers for a bit array should be a minimum of 1 CELL in size DECIMAL CELL 8 * CONSTANT AddressUnit \ 8 is bits in a byte \ calulates the power of 2 ( n -- 2^n ) : 2^ ( n -- 2^n ) 1 swap lshift ; \ compiling word, creates an array of CELL * 8 powers of 2 for masking use in the array fetch and store words. \ does: ( nindex -- nvalue ) \\ nindex is 0, 1, 2 ... giving 2^31, 2^30, 2^29 ... 2^0 : ^2MASKS (-- ) CREATE 0 AddressUnit 1- DO i 2^ , -1 +LOOP DOES> SWAP CELLS + @ ; ^2MASKS bmask \ lookup table of powers of 2 for the BIT@ and BOOL@ words \ factored out, internal use by BIT@, BOOL@ : _BIT@ ( caddr n-index -- n ) AddressUnit /MOD ( caddr n n ) CELLS ( caddr n n ) ROT + ( n caddr ) @ SWAP ( nval n ) bmask AND ( n ) ; \ given a memory buffer address caddr and index n representing at bit-wise \ index, return the value of the bit at that index as 1 | 0. : BIT@ ( caddr n-index -- n ) _BIT@ ( n ) 0= NOT ABS ; \ store a value TRUE|FALSE or 1|0, given the value (TRUE|FALSE or 1|0), an address, and a bit-wise index \ (synonym for BOOL! below) : BIT! ( n-value caddr n-index -- ) ROT ABS 1 AND ( caddr n n ) \ bounds check IF ['] OR ELSE ['] XOR THEN >R \ set masking op AddressUnit /MOD ( caddr n n ) CELLS ( caddr n n ) ROT + DUP ( n caddr caddr ) @ ROT ( caddr caddr nval ) bmask R> EXECUTE ( caddr n ) SWAP ! ; \ given a memory buffer address caddr and index n representing at bit-wise \ offset, return or store the value of the bit as TRUE|FALSE values. : BOOL@ ( caddr n-index -- flg ) _BIT@ ( n ) 0= NOT ( n ) ; \ Synonym for BIT! \ store a value TRUE|FALSE ( or 1|0 ), given the value n, a base address of the array, and a bit-wise index into it. : BOOL! ( n caddr n-index -- ) BIT! ; DECIMAL \ create the buffer: CREATE bitbuffer 2 CELLS ALLOT \ initialize buffer to all on bits: \ 4294967295 DUP bitbuffer DUP ROT ROT ! CELL+ ! -1 DUP bitbuffer DUP ROT ROT ! CELL+ ! \ store a bit at position 34 0 bitbuffer 34 BIT! 0 bitbuffer 67 bit! 0 bitbuffer 127 bit! \ for lina64 : bottomruler130 cr ." 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 " cr ." 0=========1=========2=========3=========4=========5=========6=========7=========8=========9=========0=========1=========2=========3 " cr ; : binary 2 base ! ; : (du.) <# #s #> ; : du. (du.) type bottomruler130 ; \ test it: bitbuffer 2@ binary du. decimal \ displays: 1111111111111111111111111111111111011111111111111111111111111111
沒有留言:
張貼留言