2024年10月4日 星期五

一百個例題 (86 ~ 90)


Ching-Tang Tseng
Hamilton, New Zealand
5 October 2024

第(86)個範例是仿造其他程式語言所強調之巨集指令組(macro set)觀念而設計的一個類似範例。

實際上, Forth 本身的程式設計方式,根本就各個都是巨集指令,都在設計完成後,就能給後續指令當巨集指令使用。所以,這個範例實際上是個多餘的累贅研究。

用過別種程式語言者,經常在闖入 Forth 國際論壇網頁時,胡亂發言,拿一般程式語言中使用過的術語來考驗 Forth 的熟手,通常都不太有人願意理會,但挑釁久了,也難免會有高手或老手出面修理來犯者,於是,就有這種無聊程式可被收集,這個範例就是這麼來的。

不過,要想設計出這個範例中的 macro 指令,沒有三級 Forth 程度的使用能力,也並不容易一下子就寫得出程式。程式中使用了延遲(postpone)系統執行時態(tense)的技巧解決問題。

所謂的時態,指的是系統經常只能處於執行時態(execution tense)或編譯時態(compiling tense),否則就是當機時態(exeception tense)。如果系統處於當時的時態,而程式內容能令系統改變時態時,就叫做延遲系統的執行時態了。通常的用法,都是在程式中使用一個叫做 postpone 的指令,要系統在執行到它的後續指令時,不要執行,必須改在下一個執行時態時才執行。

這樣的哲理解釋,第一次碰到的人,很難理解它的意義,因為我沒拿實際例子來解釋。

所有相關於這一類指令的詳細說明,請參考鄒靖寧老師編譯的『FORTH入門』第十一章,這本『Starting FORTH』教材中的最後一章才有解說,那時的指令名稱叫 COMPILE , [COMPILE] ,現在叫 POSTPONE 。由於這方面的哲理深奧難懂,才會被編寫到最後一章。也由於指令功能效果非凡,所以才會令 Forth 程式語言與眾不同、功能強大無比,高手也才能拿它來發揮、再創新高。

我不諱言,我自創的系統,很多地方都涉及這方面的技術應用,才可以令指令被執行的時態強行改變,也才能改變程式的語法,我才寫得出整套的 BASIC ,可以不像傳統的 BASIC 作者那樣,非得寫 BASIC 的剖析器(parser)不可,我自創的系統中直接編譯 BASIC 程式,執行起來才會快速,使用者能感覺得出程式是被直接執行的。

這個巨集指令的設計方法,是將隨後需要用到的現成指令,全當字串來看待,設計時都不會被執行,等到下一次巨集指令再被用到時,就把這些字串當指令來編進系統。執行最後完成的 foo 測試指令時,巨集效果就產生了。把字串整串執行掉的指令叫 evaluate ,執行時立即安放字串的指令叫 sliteral 。

我所謂的累贅是這樣解釋的:如果不設計這個 macro ,您就用 『 : square dup * ; 』規規矩矩地寫完 Forth 標準設計,而在此處的 foo 測試程式,仍然是寫法不變,就能完成。那麼,在 Forth 中要 macro 做什麼?根本就可以完全不用。

我在 Win32Forth 系統的低階設計部分,發現後來的版本,前人很好的源程式被大量改寫了,改為使用許多內容為低階組合語言的巨集指令,令程式更難閱讀,有錯就更為難改,搞得我後來連追蹤、查證的興趣都沒有了。
:

\ (86)macro.f This macro coding style is effect in Lina64 system.20171201

: macro 
     : char parse postpone sliteral postpone evaluate 
     postpone ; immediate 
; 
 
\ Usage is e.g. 
macro square " dup *"  ok 
: foo 5 square . ;  ok 
foo 25  ok 

第(87)個範例是一個用來印出 16 進制與 2 進制雙整數的工具指令程式。

這是一個我在發展 Lina64 系統期間所開發的發展工具,用來檢驗雙整數存在於系統中的情況。

因為在發展期間,最方便的系統觀察指令,就是 DUMP ,他能按照所給的指定位址,把後續的多少個位元組內的內容,以 16 進制的方式傾印出來。

我若在程式中輸入了雙整數,想要看看被編譯進系統後的結果,以 Dump 指令來看,只能是 16 進制的顯示,想直接比對,使用這個 DH. 與 DB. 指令來操作就很方便。

這兩個指令若想在 Lina64 中運轉得起來,有一個名叫 CO 的指令需要用到,因為要操作到文字視窗畫面來顯示輸出,它會與一般 Forth 系統不同,所以該系統中增列了這個叫做 CO 的指令,我從系統說明書中的 8.3.5 節,把說明資料也收集在範例程式的最後面,以利參考。

這個範例程式很簡單,用 Forth 系統的人,必須能直接讀懂,裡面用到的設計觀念,只是強調在改變進制時,借助於回返堆疊能夠暫存數據的能力來設計程式。臨時放一下,用完就取回來,恢復系統原來的進制狀態,如此而已。

所有的 Forth 系統在轉換記憶體內容成可印出數字時,都仍使用自古以來就用的 <# , #s , #> 等指令,只是從 1994 年的 ANSI 標準出現以後,系統就只會處理正的數字,不管負的數字了,使用時要自己注意。

反過來解釋我為什麼要設計這樣的工具?如果不用這個 DH. ,那麼,我想要檢查輸入雙整數 1.23 後的花樣時,就得執行下列操作才看得到:

1.23
Hex d. Decimal

然後對每一個輸入的雙整數,都得重複上列的操作。我嫌老打那些更換進制的命令太麻煩,所以設計了這兩個不做進制操作就能顯示結果的指令。不怕麻煩的人,可以不用理會這個設計。
:

\ (87)DH. DB. in Lina64 with special word: CO.20171202

\ Switch to hex for the duration of the definition. 
: HEX:    R> BASE @ >R  >R      HEX CO R> BASE ! ; 
: BINARY: R> BASE @ >R  >R 2 BASE ! CO R> BASE ! ;
\ An example 
: (DH.)  HEX: <# #S #> ;
: (DB.)  BINARY: <# #S #> ; 

: DH. (DH.) type ;
: DB. (DB.) TYPE ;

\ 1.2 (DH.) TYPE 
\ C OK 
\ 1.2 DH.
\ C OK


\ 8.3.5 CO
\ Name: CO
\ No stackeffect
\ Attributes:
\ Description: Return to the caller, suspending interpretation of the current definition, such that
\ when the caller exits, this definition is resumed. The return stack must not be engaged, such as
\ between >R and R> , or DO and LOOP .

第(88)個範例是一個解常數係數微分方程(Ordinary differential equations, ODE)的典型程式。

前曾述及,電腦的積分算得很準,微分算得不太準,今天就來看看解微分方程的程式技術是怎麼設計的?

範例(88-1)的程式規格才是勉強能夠符合 Win32Forth 系統的程式寫法,但標號 110 , 140 的程式中,浮點數的方次符號 ** 必須改回『 ^ 』。

範例(88)是 Lina64 中 ABC FORTH 的寫法,跟(88-1)最大的不同,就是所有浮點數的輸入方式有很明顯的差異。

凡是 1.23e4 的格式,都得以空格分開成 1.23 e 4 的方式輸入。

從這樣的差異中,大家就能體會出任何數字都只有兩種格式,一個是帶小數點的數字,另一個就是不帶小數點的整數。浮點數就可以用這種方式存在,我創作的這種浮點系統,辦到了這樣的設計,好用的很。如果不這樣設計,您可以仔細追蹤 Win32Forth 系統是如何接受浮點數的,把源程式印出來看,需要四張 A4 用紙,不是高手就很難看得懂。我在那個所有的浮點數都需要用到的 e 或 E 上動了手腳,一列程式就設計出來了,我自創的系統就能這麼方便的輸入浮點數。

早期的發展,我沒顧及後來別種程式語言都用 ** 代表 ^ 了,至少在我放棄 Win32Forth 之 ABC Forth 前,我都仍照用 ^ 。

簡單介紹微分方程電腦程式解法的大要:

教科書中的常微方程,都是經過很理想的安排,寫得整整齊齊,才能那麼容易用來教學生手解,這樣的解法,在學術上叫解析解。我踏入社會後,如果碰得到微分方程問題,就都不可能是理想的方程式了,很多 ODE 都還是根本無法手解的東西,必需簡化掉許多項後才能手解,解完,才又考慮再帶有另一項時會是怎樣?再來修正,有時解答使用的函數型式要靠猜的,才能勉強含有意義。一般手解解法的步驟,是首先把 ODE 的通解先解出來,解答便帶有未定係數,然後將起始條件或邊界條件帶入通解,再把未定係數解出來,這樣,就得到了明確的特解,算是最後結果。

用電腦程式解微分方程時,無論是 ODE 或者是偏微方程(PDE) 都一樣,解法就跟上述步驟完全不同。電腦程式解法是一開始就把無論是能不能手解的微分方程式,全都改寫成數學家憑理論推導出來的差分方式表示之等效近似逼近式,然後,直接就從起始條件或邊界條件作為演算推進的起點,一點一點的把整個指定領域中所有的對應點之數值給算出來,所以,解答都是一長串的數據組,例如一個 x 座標值對應到一個 y 的函數值。

從這麼簡單的說明,大家可以明白,電腦解法列印出來的數據是函數的趨勢,兩點之間的事情不列入考慮。因此,根本沒有值得強調有幾位數精確度的意義。把這些數據畫出圖來,才能看得清楚解答的變化趨勢。我因為長期搞電腦的數學運算,也長期使用電腦畫函數曲線圖,所以熟知這些效果,講得出其中的道理。

我保證上訴文字,您此前在那裡都沒見過。

那麼,電腦解微分方程有何好處?有的,還真不少。前面說過,真實世界中的微分方程,大部分都是無法手解、很不理想的方程式,用電腦來解這種工程問題時,沒有困擾,不需要背記那一種方程式該用那一種有點碰運氣之猜得的三角或指數函數來匹配,可以直接寫逼近式,有時需要預作微分推導,會比較麻煩而已。微分方程的數值分析解法技術也已有很多種,也可以不用進行微分處裡就能解題的應用技術,這個範例只是眾多技術中的一種,叫作泰勒級數四次微分後的匹配解法。

解的過程在起始步驟中就得先給予指定範圍與每一步驟的推進差量,當然,差量越小就可以得到越精確的解答,只是電腦計算會多一些,印出來的數據也當然多一些,如此而已。最後,電腦繪圖才最有用,趨勢用圖一看就能一目暸然。

我曾經接觸到用電腦程式解微分方程的第一個問題,是計算出從太空返回地球時,物體自由落體狀態下的極限速度。一開始,只有自己設計的固定點浮點系統可用,但就算解得的輸出數據只有三位數精確度可言,拿來繪圖,卻也看得出很理想的變化趨勢了。

這幾十年我解過無數次的微分方程式,自己覺得就算拿來搞武器的線上直接控制,也能有用。掌握趨勢才是解微分方程的重點,小數點後面三位數,就有千分之一的精確度,打什麼目標需要這麼準?我搞過打軍艦的魚雷,解析度才 12 度左右就夠了,把我設計的系統裝上武器,可以直接使用。
:

\ (88)Taylor series method to solve ODE(order 4 ) in lina64 ./f5106

\ data M,h,t,x/200,0.01,-1.0,3.0/

2 integers k m
7 reals h t x x1 x2 x3 x4

: taylor basic

10 print " * "
20 print " *, Taylor series method (order 4) "
30 print " *, Section 8.1, Kincaid-Cheney "
40 print " * "

50 let m = 200
60 let { h =  0.01 e 0  }
70 let { t =  -1.0 e 0  }
80 let { x =   3.0 e 0  }
90 print 0 , { t ,  x }

100 for k = 1 to m
110 let { x1 = cos ( t ) - sin ( x ) + t ** 2.0 e 0  }
120 let { x2 = negate ( sin ( t ) ) - x1 * cos ( x ) + 2.0 e 0 * t }     
130 let { x3 = negate ( cos ( t ) ) - x2 * cos ( x ) + ( x1 * x1 ) * sin ( x ) + 2.0 e 0 }      
140 let { x4 = sin ( t ) + ( x3 *8 3.0 e 0 - x3 ) * cos ( x ) + 3.0 e 0 * x1 * x2 * sin ( x ) } 
150 let {  x = x + h * ( x1 + ( h / 2.0 e 0 ) * ( x2 + ( h / 6.0 e 0 ) * ( x3 + ( h / 24.0 e 0 ) * x4 ) ) ) }
160 let {  t = t + h }      
170 print k , { t , x }
200 next k

210 end ;

\ (88-1)Taylor series method to solve ODE(order 4) in gforth64 abc forth

2 integers k m
7 reals h t x x1 x2 x3 x4

: taylor ( -- )
basic
10 print " * "
20 print " * Taylor series method (order 4) "
30 print " * Section 8.1, Kincaid-Cheney "
40 print " * "
50 let m = 200
60 let { h =  0.01e0  }
70 let { t =  -1.0e0  }
80 let { x =   3.0e0  }
90 print 0 , { t , x }
100 for k = 1 to m
110 let { x1 = cos ( t ) - sin ( x ) + t ** 2.0e0 }
120 let { x2 = negate ( sin ( t ) ) - x1 * cos ( x ) + 2.0e0 * t }     
130 let { x3 = negate ( cos ( t ) ) - x2 * cos ( x ) + ( x1 * x1 ) * sin ( x ) + 2.0e0 }      
140 let { x4 = sin ( t ) + ( x3 ** 3.0e0 - x3 ) * cos ( x ) + 3.0e * x1 * x2 * sin ( x ) } 
150 let {  x = x + h * ( x1 + ( h / 2.0e0 ) * ( x2 + ( h / 6.0e0 ) * ( x3 + ( h / 24.0e0 ) * x4 ) ) ) }
160 let {  t = t + h }      
170 print k , { t , x }
200 next k
210 end ;

\s

\ Reference:
\ c
\ c     Second Edition
\ c     Numerical Analysis:
\ c     The Mathematics of Scientific Computing
\ c     D.R. Kincaid & E.W. Cheney
\ c     Brooks/Cole Publ., 1996
\ c     ISBN 0-534-33892-5
\ c     COPYRIGHT (c) 1996
\ c
\ c     Section 8.2
\ c
\ c     Solving the initial value problem using Taylor Series
\ c
\ c
\ c     file: taylor.f
\ c
\       data M,h,t,x/200,0.01,-1.0,3.0/
\ c
\       print *
\       print *,' Taylor series method (order 4) '
\       print *,' Section 8.1, Kincaid-Cheney'
\       print *
\       print 3,'k','t','x'
\       print 4,0,t,x
\ c
\       do 2 k=1,M
\          x1 = cos(t) - sin(x) + t**2.0 
\          x2 = -sin(t) - x1*cos(x) + 2.0*t       
\          x3 = -cos(t) - x2*cos(x) + (x1**2.0)*sin(x) + 2.0       
\          x4 = sin(t) + ((x3)**3.0 -x3)*cos(x) + 3.0*x1*x2*sin(x) 
\           x = x + h*(x1 + (h/2.)*(x2 + (h/6.)*(x3 + (h/24.)*x4)))
\           t = t + h       
\           print 4,k,t,x 
\  2    continue
\  c
\  3    format(a6,a9,a15)
\  4    format(1x,i5,2x,e13.6,2x,e13.6)      
\       stop
\       end 

include taylor redefined k   ok
taylor 
* 
* Taylor series method (order 4) 
* Section 8.1, Kincaid-Cheney 
* 
                     0      -1.00000000000000E0        3.00000000000000E0  
                     1      -9.90000000000000E-1       3.01400331867272E0  
                     2      -9.80000000000000E-1       3.02803125373840E0  
                     3      -9.70000000000000E-1       3.04208574304848E0  
                     4      -9.60000000000000E-1       3.05616870908915E0  
                     5      -9.50000000000000E-1       3.07028205863460E0  
                     6      -9.40000000000000E-1       3.08442768236222E0  
                     7      -9.30000000000000E-1       3.09860745442929E0  
                     8      -9.20000000000000E-1       3.11282323201108E0  
                     9      -9.10000000000000E-1       3.12707685479982E0  
                    10      -9.00000000000000E-1       3.14137014446445E0  
                    11      -8.90000000000000E-1       3.15570490407070E0  
                    12      -8.80000000000000E-1       3.17008291746145E0  
                    13      -8.70000000000000E-1       3.18450594859697E0  
                    14      -8.60000000000000E-1       3.19897574085496E0  
                    15      -8.50000000000000E-1       3.21349401629017E0  
                    16      -8.40000000000000E-1       3.22806247485362E0  
                    17      -8.30000000000000E-1       3.24268279357115E0  
                    18      -8.20000000000000E-1       3.25735662568157E0  
                    19      -8.10000000000000E-1       3.27208559973412E0  
                    20      -8.00000000000000E-1       3.28687131864576E0  
                    21      -7.90000000000000E-1       3.30171535871815E0  
                    22      -7.80000000000000E-1       3.31661926861472E0  
                    23      -7.70000000000000E-1       3.33158456829826E0  
                    24      -7.60000000000000E-1       3.34661274792935E0  
                    25      -7.50000000000000E-1       3.36170526672629E0  
                    26      -7.40000000000000E-1       3.37686355178704E0  
                    27      -7.30000000000000E-1       3.39208899687415E0  
                    28      -7.20000000000000E-1       3.40738296116338E0  
                    29      -7.10000000000000E-1       3.42274676795710E0  
                    30      -7.00000000000000E-1       3.43818170336371E0  
                    31      -6.90000000000000E-1       3.45368901494417E0  
                    32      -6.80000000000000E-1       3.46926991032735E0  
                    33      -6.70000000000000E-1       3.48492555579552E0  
                    34      -6.60000000000000E-1       3.50065707484203E0  
                    35      -6.50000000000000E-1       3.51646554670289E0  
                    36      -6.40000000000000E-1       3.53235200486450E0  
                    37      -6.30000000000000E-1       3.54831743554973E0  
                    38      -6.20000000000000E-1       3.56436277618494E0  
                    39      -6.10000000000000E-1       3.58048891385056E0  
                    40      -6.00000000000000E-1       3.59669668371799E0  
                    41      -5.90000000000000E-1       3.61298686747618E0  
                    42      -5.80000000000000E-1       3.62936019175081E0  
                    43      -5.70000000000000E-1       3.64581732651983E0  
                    44      -5.60000000000000E-1       3.66235888352892E0  
                    45      -5.50000000000000E-1       3.67898541471076E0  
                    46      -5.40000000000000E-1       3.69569741061222E0  
                    47      -5.30000000000000E-1       3.71249529883380E0  
                    48      -5.20000000000000E-1       3.72937944248577E0  
                    49      -5.10000000000000E-1       3.74635013866571E0  
                    50      -5.00000000000000E-1       3.76340761696235E0  
                    51      -4.90000000000000E-1       3.78055203799070E0  
                    52      -4.80000000000000E-1       3.79778349196384E0  
                    53      -4.70000000000000E-1       3.81510199730654E0  
                    54      -4.60000000000000E-1       3.83250749931645E0  
                    55      -4.49999999999999E-1       3.84999986887835E0  
                    56      -4.40000000000000E-1       3.86757890123739E0  
                    57      -4.30000000000000E-1       3.88524431483701E0  
                    58      -4.19999999999999E-1       3.90299575022768E0  
                    59      -4.09999999999999E-1       3.92083276905230E0  
                    60      -3.99999999999999E-1       3.93875485311441E0  
                    61      -3.89999999999999E-1       3.95676140353516E0  
                    62      -3.79999999999999E-1       3.97485174000507E0  
                    63      -3.69999999999999E-1       3.99302510013663E0  
                    64      -3.59999999999999E-1       4.01128063892351E0  
                    65      -3.49999999999999E-1       4.02961742831219E0  
                    66      -3.39999999999999E-1       4.04803445689171E0  
                    67      -3.29999999999999E-1       4.06653062970685E0  
                    68      -3.19999999999999E-1       4.08510476820015E0  
                    69      -3.09999999999999E-1       4.10375561028756E0  
                    70      -2.99999999999999E-1       4.12248181057268E0  
                    71      -2.89999999999999E-1       4.14128194070366E0  
                    72      -2.79999999999999E-1       4.16015448987716E0  
                    73      -2.69999999999999E-1       4.17909786549267E0  
                    74      -2.59999999999999E-1       4.19811039396072E0  
                    75      -2.49999999999999E-1       4.21719032166743E0  
                    76      -2.39999999999999E-1       4.23633581609803E0  
                    77      -2.29999999999999E-1       4.25554496712079E0  
                    78      -2.19999999999999E-1       4.27481578843282E0  
                    79      -2.09999999999999E-1       4.29414621916815E0  
                    80      -1.99999999999999E-1       4.31353412566839E0  
                    81      -1.89999999999999E-1       4.33297730341515E0  
                    82      -1.79999999999999E-1       4.35247347912321E0  
                    83      -1.69999999999999E-1       4.37202031299245E0  
                    84      -1.59999999999999E-1       4.39161540111606E0  
                    85      -1.49999999999999E-1       4.41125627804200E0  
                    86      -1.39999999999999E-1       4.43094041948356E0  
                    87      -1.29999999999999E-1       4.45066524517488E0  
                    88      -1.19999999999999E-1       4.47042812186596E0  
                    89      -1.09999999999999E-1       4.49022636645160E0  
                    90      -9.99999999999992E-2       4.51005724922771E0  
                    91      -8.99999999999993E-2       4.52991799726795E0  
                    92      -7.99999999999993E-2       4.54980579791316E0  
                    93      -6.99999999999993E-2       4.56971780236541E0  
                    94      -5.99999999999993E-2       4.58965112937790E0  
                    95      -4.99999999999993E-2       4.60960286903159E0  
                    96      -3.99999999999992E-2       4.62957008658907E0  
                    97      -2.99999999999992E-2       4.64954982641547E0  
                    98      -1.99999999999992E-2       4.66953911595623E0  
                    99      -9.99999999999925E-3       4.68953496976106E0  
                   100      7.52869988573934E-16       4.70953439354318E0  
                   101      1.00000000000008E-2       4.72953438826286E0  
                   102      2.00000000000008E-2       4.74953195422398E0  
                   103      3.00000000000008E-2       4.76952409517247E0  
                   104      4.00000000000008E-2       4.78950782238533E0  
                   105      5.00000000000008E-2       4.80948015873896E0  
                   106      6.00000000000008E-2       4.82943814274579E0  
                   107      7.00000000000008E-2       4.84937883254824E0  
                   108      8.00000000000007E-2       4.86929930985933E0  
                   109      9.00000000000007E-2       4.88919668383937E0  
                   110      1.00000000000001E-1       4.90906809489885E0  
                   111      1.10000000000001E-1       4.92891071841753E0  
                   112      1.20000000000001E-1       4.94872176837072E0  
                   113      1.30000000000001E-1       4.96849850085358E0  
                   114      1.40000000000001E-1       4.98823821749537E0  
                   115      1.50000000000001E-1       5.00793826875578E0  
                   116      1.60000000000001E-1       5.02759605709597E0  
                   117      1.70000000000001E-1       5.04720904001792E0  
                   118      1.80000000000001E-1       5.06677473296597E0  
                   119      1.90000000000001E-1       5.08629071208515E0  
                   120      2.00000000000001E-1       5.10575461683161E0  
                   121      2.10000000000001E-1       5.12516415243122E0  
                   122      2.20000000000001E-1       5.14451709218274E0  
                   123      2.30000000000001E-1       5.16381127960298E0  
                   124      2.40000000000001E-1       5.18304463041180E0  
                   125      2.50000000000001E-1       5.20221513435567E0  
                   126      2.60000000000001E-1       5.22132085686877E0  
                   127      2.70000000000001E-1       5.24035994057177E0  
                   128      2.80000000000001E-1       5.25933060660847E0  
                   129      2.90000000000001E-1       5.27823115582151E0  
                   130      3.00000000000001E-1       5.29705996976865E0  
                   131      3.10000000000001E-1       5.31581551158175E0  
                   132      3.20000000000001E-1       5.33449632667108E0  
                   133      3.30000000000001E-1       5.35310104327801E0  
                   134      3.40000000000001E-1       5.37162837287958E0  
                   135      3.50000000000001E-1       5.39007711044894E0  
                   136      3.60000000000001E-1       5.40844613457587E0  
                   137      3.70000000000001E-1       5.42673440745194E0  
                   138      3.80000000000001E-1       5.44494097472537E0  
                   139      3.90000000000001E-1       5.46306496523059E0  
                   140      4.00000000000001E-1       5.48110559059800E0  
                   141      4.10000000000001E-1       5.49906214474942E0  
                   142      4.20000000000001E-1       5.51693400328501E0  
                   143      4.30000000000001E-1       5.53472062276751E0  
                   144      4.40000000000001E-1       5.55242153990973E0  
                   145      4.50000000000001E-1       5.57003637067127E0  
                   146      4.60000000000001E-1       5.58756480927059E0  
                   147      4.70000000000001E-1       5.60500662711828E0  
                   148      4.80000000000001E-1       5.62236167167787E0  
                   149      4.90000000000001E-1       5.63962986525971E0  
                   150      5.00000000000001E-1       5.65681120375425E0  
                   151      5.10000000000001E-1       5.67390575531022E0  
                   152      5.20000000000001E-1       5.69091365896359E0  
                   153      5.30000000000001E-1       5.70783512322274E0  
                   154      5.40000000000001E-1       5.72467042461543E0  
                   155      5.50000000000001E-1       5.74141990620275E0  
                   156      5.60000000000001E-1       5.75808397606512E0  
                   157      5.70000000000001E-1       5.77466310576543E0  
                   158      5.80000000000001E-1       5.79115782879393E0  
                   159      5.90000000000001E-1       5.80756873899947E0  
                   160      6.00000000000001E-1       5.82389648901151E0  
                   161      6.10000000000001E-1       5.84014178865698E0  
                   162      6.20000000000001E-1       5.85630540337598E0  
                   163      6.30000000000001E-1       5.87238815264011E0  
                   164      6.40000000000001E-1       5.88839090837694E0  
                   165      6.50000000000001E-1       5.90431459340389E0  
                   166      6.60000000000001E-1       5.92016017987487E0  
                   167      6.70000000000001E-1       5.93592868774237E0  
                   168      6.80000000000001E-1       5.95162118323788E0  
                   169      6.90000000000001E-1       5.96723877737319E0  
                   170      7.00000000000001E-1       5.98278262446484E0  
                   171      7.10000000000001E-1       5.99825392068402E0  
                   172      7.20000000000001E-1       6.01365390263375E0  
                   173      7.30000000000001E-1       6.02898384595535E0  
                   174      7.40000000000001E-1       6.04424506396569E0  
                   175      7.50000000000001E-1       6.05943890632684E0  
                   176      7.60000000000001E-1       6.07456675774938E0  
                   177      7.70000000000001E-1       6.08963003673064E0  
                   178      7.80000000000001E-1       6.10463019432890E0  
                   179      7.90000000000001E-1       6.11956871297446E0  
                   180      8.00000000000001E-1       6.13444710531846E0  
                   181      8.10000000000001E-1       6.14926691312004E0  
                   182      8.20000000000001E-1       6.16402970617256E0  
                   183      8.30000000000001E-1       6.17873708126922E0  
                   184      8.40000000000001E-1       6.19339066120860E0  
                   185      8.50000000000001E-1       6.20799209384029E0  
                   186      8.60000000000001E-1       6.22254305115098E0  
                   187      8.70000000000001E-1       6.23704522839102E0  
                   188      8.80000000000001E-1       6.25150034324157E0  
                   189      8.90000000000001E-1       6.26591013502245E0  
                   190      9.00000000000001E-1       6.28027636394051E0  
                   191      9.10000000000001E-1       6.29460081037847E0  
                   192      9.20000000000001E-1       6.30888527422417E0  
                   193      9.30000000000001E-1       6.32313157424002E0  
                   194      9.40000000000001E-1       6.33734154747233E0  
                   195      9.50000000000001E-1       6.35151704870052E0  
                   196      9.60000000000001E-1       6.36565994992567E0  
                   197      9.70000000000001E-1       6.37977213989839E0  
                   198      9.80000000000001E-1       6.39385552368558E0  
                   199      9.90000000000001E-1       6.40791202227567E0  
                   200      1.00000000000000E0       6.42194357222237E0   ok

第(89)個範例是兩套設計出常用但非標準指令 within ( n1 n2 n3 — flag ) 的方法,設計方式帶有技巧。

使用 Forth 系統應該養成一點寫程式時的好習慣,以免日後回頭追蹤時,給自己帶來困擾。這個習慣,就是每當設計任何新指令時,要記得在指令名稱的同一列內,加添表示這個指令執行前後,堆疊數據的變化狀況,例如:此例為 ( n1 n2 n3 — flag )。

within 的功能是判斷出 n1 是不是介於 [n2, n3) 之間?這是標準嚴謹的階梯函數之數學表示法,寫成數學式則為:

n2 =< n1 < n3

亦即,n1 屬於 [n2, n3) 之間的所有數字,包括 n2 但不包括 n3 ,這樣的規格,可以從微積分課本第一章中獲得。

程式執行結果,則會得到一個旗號,為真時得 -1 ,為假時則得 0 。

一般而言,常人在得到上述問題時的直接反應,會是去採用邏輯比較法來設計程式,也就是採用假如怎樣就會怎樣的思考方式來設計程式,程式中就會出現許多 IF ..... ELSE ..... THEN 的結構,這個範例程式中卻完全沒有。

我經常瀏覽國際論壇上的討論,主要目的就是去發掘一些名家的設計,可以發現許多採用非常規思考方式所設計出來的漂亮結果,這個範例就是在這種情況下所獲得的。

設計者完全不用邏輯比較結構,卻能設計得出這個必須至少得用兩次比較才能產生正確輸出結果的程式。至少得用兩次的比較,被採用一次減法運算,與另一個無號數的比較指令 u< ,實現出了等效的結果。

能設計出這種技術程式者,算是高手,簡化了程式量,也讓程式執行速度大量加速,很值得欽佩。

這幾十年來,我的個人筆記已經累積了 20 幾本,本本都記錄了這類的成就,筆記中除了自己的思考記錄,其餘的,就都是名家設計的精華。我每次為新系統發展自創部份時,都會拿出來參考,好幾本筆記本都已用到脫頁了,表示它們長期貢獻過設計工作,我很珍惜。

這兩套設計方法都很漂亮, within1 採用將過渡期的暫態數據暫存於回返堆疊的方法來設計程式,我後來覺得有些新式的 Forth 系統,把回返堆疊設計得很不像樣,還猛用,總有一天會出問題,所以,我還是按照 Forth 的傳統精神,改寫出規規矩矩全只使用堆疊指令的方式,設計出了 within2 ,這就是這兩個功效一樣的程式設計上之差異。

想要測試指令也很簡單,這種事我常做,在此提醒大家,處理標準數學問題時,千萬要記得『數字有正負,範圍有邊界』,測試程式時,它們都是經常會被忽略的問題,務必永遠記得,此範例都能測試得到的ㄧ序列測試如下:

-7 -5 5 within .
-5 -5 5 within .
0 -5 5 within .
5 -5 5 within .
7 -5 5 within .

如果您將上列測試執行完畢後,所得 0 或 -1 的旗號結果,都符合要求,那麼,這樣的設計,就算是絕對沒有問題了。
:

\ (89)high level within

: within1 ( n1 n2 n3 -- flag ) 
   over - >r - r> u< ;
: within2
  OVER - -ROT - SWAP U< ; 

第(90)個範例是有關陣列(array)、矩陣(matrix)及張量(tensor)資料結構的測試程式。

由於這三種資料結構又可分為三種不同的內容:整數、實數、複數。結構內容也會不同,所以都必須測試,測試的環境,是 64 位元的 gForth 系統,測試的方法,是使用指定數字與亂數置入之後,再按秩序整齊的列印出來,印出的同時,就根據各種維度的指標,來換算出應該取得的資料結構之位址,取出內容,直接印出。

這個程式不能在 Win32Forth 系統中使用,主要的影響,是整數指標不能在實數與複數的運作環境中使用。

這是一個很麻煩而難以處理的問題,要使用令系統在處理當時能忽略掉指標為整數而不是實數或複數的特殊技術。該忽略幾次?隨維度為多少而變化。還有,有些 Forth 系統裡,恆把整數 -1 , 0 , 1 , 2 都設計成常數,以便提高執行速度。在我自創的 ABC Forth 系統中,這些問題都造成很大的困擾,所以設計出來的應用規則,就會有很大的差異。

上述涉及的問題,在不同的系統中,解決問題的方法都不一樣,我沒有辦法把它們一一列示於此。關於這方面的技術,我就不談了。

我在發展有維度的資料結構設計時,進展到矩陣階段,發現原在 Win32Forth 中的設計方式不太理想,原始的設計格式是採用了 Forth 鼻祖 Charles H. Moore 的規格,他很省記憶體,又喜歡凡事都倒過來用,結果,對矩陣二維度以上的東西,用起來就會有麻煩。

矩陣在數學上有其獨特的運算,如點乘(dot production)或叉乘(cross production),運算時,都得憑維度指標來取得元素位址,才能有正確的數字可用。如果維度在資料結構中存放不足或秩序顛倒,設計起來,就有很大的麻煩。我從 32 位元環境走進 64 位元環境後,就完全放棄了原來的設計規格,改採自己在受盡折磨後才體會出來的方法,修正設計。這也是為什麼我在設計出 gForth64 的創作後,仍須重新測試這批資料結構的原因。
:

\ (90)A M T test in gforth64 ABC.20171202

4 (array) aa
3 4 (matrix) mm
2 2 2 (tensor) tt
3 integers i j k

: initaa basic
10 for i = 0 to 4
20 let aa ( i ) = i 
30 next i 
40 end ;

: initmm basic
10 for i = 0 to 3
20 for j = 0 to 4
30 let mm ( i j ) = i * j
40 next j
50 next i
60 end ;

: inittt basic
10 for i = 0 to 2
20 for j = 0 to 2
30 for k = 0 to 2
40 let tt ( i j k ) = i * j * k
50 next k
60 next j
70 next i
80 end ;

: .aa basic
10 for i = 0 to 4
20 print " aa( " ; i ; " )= " , aa ( i )
30 next i
40 end ;

: .mm basic
10 for i = 0 to 3
20 for j = 0 to 4
30 print " mm( " ; i ; j ; " )= " , mm ( i j )
40 next j
50 next i
60 end ;

: .tt basic
10 for i = 0 to 2 
20 for j = 0 to 2
30 for k = 0 to 2
40 print " tt(" ; i ; j ; k ; " ) = " , tt ( i j k )
50 next k
60 next j
70 next i 
80 end ;

4 array faa
3 4 matrix fmm
2 2 2 tensor ftt

: initfaa basic
10 for i = 0 to 4
20 let { faa ( i ) = I>R ( i ) }
30 next i 
40 end ;

: initfmm basic
10 for i = 0 to 3
20 for j = 0 to 4
30 let { fmm ( i j ) = I>R ( i ) * I>R ( j ) }
40 next j
50 next i
60 end ;

: initftt basic
10 for i = 0 to 2
20 for j = 0 to 2
30 for k = 0 to 2
40 let { ftt ( i j k ) = I>R ( i ) * I>R ( j ) * I>R ( k ) }
50 next k
60 next j
70 next i
80 end ;

: .faa basic
10 for i = 0 to 4
20 print " faa( " ; i ; " )= " , { faa ( i ) }
30 next i
40 end ;

: .fmm basic
10 for i = 0 to 3
20 for j = 0 to 4
30 print " fmm( " ; i ; j ; " )= " , { fmm ( i j ) }
40 next j
50 next i
60 end ;

: .ftt basic
10 for i = 0 to 2
20 for j = 0 to 2
30 for k = 0 to 2
40 print " ftt( " ; i ; j ; k ; " ) = " ,  { ftt ( i j k ) }
50 next k
60 next j
70 next i
80 end ;

4 [array] zaa
3 4 [matrix] zmm
2 2 2 [tensor] ztt

: initzaa basic
10 for i = 0 to 4
20 let Z{ zaa ( i ) = 1.0e0 - 2.0e0 ii }Z
30 next i
40 end ;

: initzmm basic
10 for i = 0 to 3
20 for j = 0 to 4
30 let Z{ zmm ( i j ) = ZRAND }Z
40 next j
50 next i
60 end ;

: initztt basic
10 for i = 0 to 2
20 for j = 0 to 2
30 for k = 0 to 2
40 let z{ ztt ( i j k ) = ZRAND }Z
50 next k
60 next j
70 next i
80 end ;

: .zaa basic
10 for i = 0 to 4
20 print " zaa( " ; i ; " )= " , Z{ zaa ( i ) }Z
30 next i
40 end ;

: .zmm basic
10 for i = 0 to 3
20 for j = 0 to 4
30 print " zmm( " ; i ; j ; " )= " , Z{ zmm ( i j ) }Z
40 next j
50 next i
60 end ;

: .ztt basic
10 for i = 0 to 2
20 for j = 0 to 2
30 for k = 0 to 2
40 print " ztt( " ; i ; j ; k ; " ) = " ,  Z{ ztt ( i j k ) }Z
50 next k
60 next j
70 next i
80 end ;


: main basic

10 run initaa
20 run initmm
40 run inittt
50 run .aa cr
60 run .mm cr
70 run .tt cr

110 run initfaa
120 run initfmm
130 run initftt
140 run .faa cr
150 run .fmm cr
160 run .ftt cr

210 run initzaa
220 run initzmm
230 run initztt
240 run .zaa cr
250 run .zmm cr
260 run .ztt cr

300 end ;

main

\s
ching@center:~$ cd gforth
ching@center:~/gforth$ ./abc06
Gforth 0.7.2, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
include testamt redefined i  redefined j  redefined k   ok
main 
aa( 0 )=                      0 
aa( 1 )=                      1 
aa( 2 )=                      2 
aa( 3 )=                      3 
aa( 4 )=                      4 

mm( 0 0 )=                      0 
mm( 0 1 )=                      0 
mm( 0 2 )=                      0 
mm( 0 3 )=                      0 
mm( 0 4 )=                      0 
mm( 1 0 )=                      0 
mm( 1 1 )=                      1 
mm( 1 2 )=                      2 
mm( 1 3 )=                      3 
mm( 1 4 )=                      4 
mm( 2 0 )=                      0 
mm( 2 1 )=                      2 
mm( 2 2 )=                      4 
mm( 2 3 )=                      6 
mm( 2 4 )=                      8 
mm( 3 0 )=                      0 
mm( 3 1 )=                      3 
mm( 3 2 )=                      6 
mm( 3 3 )=                      9 
mm( 3 4 )=                     12 

tt(0 0 0 ) =                      0 
tt(0 0 1 ) =                      0 
tt(0 0 2 ) =                      0 
tt(0 1 0 ) =                      0 
tt(0 1 1 ) =                      0 
tt(0 1 2 ) =                      0 
tt(0 2 0 ) =                      0 
tt(0 2 1 ) =                      0 
tt(0 2 2 ) =                      0 
tt(1 0 0 ) =                      0 
tt(1 0 1 ) =                      0 
tt(1 0 2 ) =                      0 
tt(1 1 0 ) =                      0 
tt(1 1 1 ) =                      1 
tt(1 1 2 ) =                      2 
tt(1 2 0 ) =                      0 
tt(1 2 1 ) =                      2 
tt(1 2 2 ) =                      4 
tt(2 0 0 ) =                      0 
tt(2 0 1 ) =                      0 
tt(2 0 2 ) =                      0 
tt(2 1 0 ) =                      0 
tt(2 1 1 ) =                      2 
tt(2 1 2 ) =                      4 
tt(2 2 0 ) =                      0 
tt(2 2 1 ) =                      4 
tt(2 2 2 ) =                      8 

faa( 0 )=      0.00000000000000E0  
faa( 1 )=      1.00000000000000E0  
faa( 2 )=      2.00000000000000E0  
faa( 3 )=      3.00000000000000E0  
faa( 4 )=      4.00000000000000E0  

fmm( 0 0 )=      0.00000000000000E0  
fmm( 0 1 )=      0.00000000000000E0  
fmm( 0 2 )=      0.00000000000000E0  
fmm( 0 3 )=      0.00000000000000E0  
fmm( 0 4 )=      0.00000000000000E0  
fmm( 1 0 )=      0.00000000000000E0  
fmm( 1 1 )=      1.00000000000000E0  
fmm( 1 2 )=      2.00000000000000E0  
fmm( 1 3 )=      3.00000000000000E0  
fmm( 1 4 )=      4.00000000000000E0  
fmm( 2 0 )=      0.00000000000000E0  
fmm( 2 1 )=      2.00000000000000E0  
fmm( 2 2 )=      4.00000000000000E0  
fmm( 2 3 )=      6.00000000000000E0  
fmm( 2 4 )=      8.00000000000000E0  
fmm( 3 0 )=      0.00000000000000E0  
fmm( 3 1 )=      3.00000000000000E0  
fmm( 3 2 )=      6.00000000000000E0  
fmm( 3 3 )=      9.00000000000000E0  
fmm( 3 4 )=      1.20000000000000E1  

ftt( 0 0 0 ) =      0.00000000000000E0  
ftt( 0 0 1 ) =      0.00000000000000E0  
ftt( 0 0 2 ) =      0.00000000000000E0  
ftt( 0 1 0 ) =      0.00000000000000E0  
ftt( 0 1 1 ) =      0.00000000000000E0  
ftt( 0 1 2 ) =      0.00000000000000E0  
ftt( 0 2 0 ) =      0.00000000000000E0  
ftt( 0 2 1 ) =      0.00000000000000E0  
ftt( 0 2 2 ) =      0.00000000000000E0  
ftt( 1 0 0 ) =      0.00000000000000E0  
ftt( 1 0 1 ) =      0.00000000000000E0  
ftt( 1 0 2 ) =      0.00000000000000E0  
ftt( 1 1 0 ) =      0.00000000000000E0  
ftt( 1 1 1 ) =      1.00000000000000E0  
ftt( 1 1 2 ) =      2.00000000000000E0  
ftt( 1 2 0 ) =      0.00000000000000E0  
ftt( 1 2 1 ) =      2.00000000000000E0  
ftt( 1 2 2 ) =      4.00000000000000E0  
ftt( 2 0 0 ) =      0.00000000000000E0  
ftt( 2 0 1 ) =      0.00000000000000E0  
ftt( 2 0 2 ) =      0.00000000000000E0  
ftt( 2 1 0 ) =      0.00000000000000E0  
ftt( 2 1 1 ) =      2.00000000000000E0  
ftt( 2 1 2 ) =      4.00000000000000E0  
ftt( 2 2 0 ) =      0.00000000000000E0  
ftt( 2 2 1 ) =      4.00000000000000E0  
ftt( 2 2 2 ) =      8.00000000000000E0  

zaa( 0 )=      1.00000000000000E0 - 2.00000000000000E0 ii  
zaa( 1 )=      1.00000000000000E0 - 2.00000000000000E0 ii  
zaa( 2 )=      1.00000000000000E0 - 2.00000000000000E0 ii  
zaa( 3 )=      1.00000000000000E0 - 2.00000000000000E0 ii  
zaa( 4 )=      1.00000000000000E0 - 2.00000000000000E0 ii  

zmm( 0 0 )=      2.18418296993905E-1 + 9.56317576559408E-1 ii  
zmm( 0 1 )=      8.29509233976486E-1 + 5.61695442796543E-1 ii  
zmm( 0 2 )=      4.15307081497883E-1 + 6.61187349195214E-2 ii  
zmm( 0 3 )=      2.57577792395641E-1 + 1.09956793538275E-1 ii  
zmm( 0 4 )=      4.38289977814206E-2 + 6.33965712335876E-1 ii  
zmm( 1 0 )=      6.17272290688600E-2 + 4.49538960330905E-1 ii  
zmm( 1 1 )=      4.01306281518799E-1 + 7.54673486461245E-1 ii  
zmm( 1 2 )=      7.97286954148340E-1 + 1.83837115850224E-3 ii  
zmm( 1 3 )=      8.97504060947105E-1 + 3.50752337999061E-1 ii  
zmm( 1 4 )=      9.45447502166707E-2 + 1.36168915841807E-2 ii  
zmm( 2 0 )=      8.59096855325204E-1 + 8.40847450700052E-1 ii  
zmm( 2 1 )=      1.23103915771052E-1 + 7.51236407436075E-3 ii  
zmm( 2 2 )=      2.60302997781105E-1 + 9.12483707029598E-1 ii  
zmm( 2 3 )=      1.13664046448499E-1 + 3.51628659922457E-1 ii  
zmm( 2 4 )=      8.22887316729355E-1 + 2.67132270274280E-1 ii  
zmm( 3 0 )=      6.92066499820010E-1 + 5.61662474908709E-1 ii  
zmm( 3 1 )=      8.61215790669069E-1 + 4.53793775035904E-1 ii  
zmm( 3 2 )=      9.11977028433223E-1 + 5.97916877175643E-1 ii  
zmm( 3 3 )=      1.88954691024942E-1 + 7.61492056195388E-1 ii  
zmm( 3 4 )=      3.96988475880115E-1 + 1.85314117085801E-1 ii  

ztt( 0 0 0 ) =      5.74365861050024E-1 + 3.67026667747193E-1 ii  
ztt( 0 0 1 ) =      6.17204827078248E-1 + 3.61528704111245E-1 ii  
ztt( 0 0 2 ) =      2.12929997692318E-1 + 7.14471214783597E-1 ii  
ztt( 0 1 0 ) =      1.17706867921030E-1 + 2.99329148744852E-1 ii  
ztt( 0 1 1 ) =      8.25002954725643E-1 + 8.24660073884139E-1 ii  
ztt( 0 1 2 ) =      6.18617707220194E-2 + 7.10780524979709E-1 ii  
ztt( 0 2 0 ) =      8.82833339685031E-2 + 7.77994008631443E-1 ii  
ztt( 0 2 1 ) =      7.45303068657081E-1 + 3.08674919562729E-1 ii  
ztt( 0 2 2 ) =      8.99373090779117E-1 + 7.63536724617489E-1 ii  
ztt( 1 0 0 ) =      7.61730646137954E-1 + 4.06969640593496E-1 ii  
ztt( 1 0 1 ) =      9.38749454887002E-1 + 5.62088285834570E-1 ii  
ztt( 1 0 2 ) =      1.78200216115546E-2 + 5.01103225397460E-1 ii  
ztt( 1 1 0 ) =      4.19092551068912E-2 + 3.68850581519702E-1 ii  
ztt( 1 1 1 ) =      2.71723601627966E-1 + 8.58572561227983E-1 ii  
ztt( 1 1 2 ) =      2.90365587123840E-2 + 1.74422790377598E-2 ii  
ztt( 1 2 0 ) =      1.52383787628442E-1 + 1.14318671223856E-1 ii  
ztt( 1 2 1 ) =      3.53907259345943E-1 + 1.19307827260023E-1 ii  
ztt( 1 2 2 ) =      2.06652759204923E-1 + 2.12923957134096E-1 ii  
ztt( 2 0 0 ) =      6.12947552750328E-1 + 8.09519074768535E-1 ii  
ztt( 2 0 1 ) =      5.87089634773829E-1 + 2.15491643741490E-1 ii  
ztt( 2 0 2 ) =      7.68056363225010E-1 + 7.23296722734020E-1 ii  
ztt( 2 1 0 ) =      4.48018990665683E-1 + 8.55176118135069E-1 ii  
ztt( 2 1 1 ) =      9.45017496098307E-1 + 9.09056924241156E-1 ii  
ztt( 2 1 2 ) =      5.19725721105806E-1 + 3.01946252725062E-2 ii  
ztt( 2 2 0 ) =      4.81066955011835E-1 + 2.92312883908075E-1 ii  
ztt( 2 2 1 ) =      9.02639843012504E-1 + 6.67841511158199E-1 ii  
ztt( 2 2 2 ) =      4.12278035847600E-1 + 1.56948490607063E-1 ii  
 ok

沒有留言: