字體:小 中 大 | |
|
|
2013/07/14 13:01:47瀏覽3798|回應0|推薦0 | |
Macros 翻譯中文是什麼,查字典就是「宏」。有的書也寫為「巨集」。這二者好像含意搭不起來,至少我無法從中文的意義和Macro的意義聯接不上。照Marco的字義來看,是「大」的意思,所以「宏」和「大」的意義相關,但在程式設計上,也不是這個意義。而「巨集」本身在中文詞彙中本來就沒有意義,也很難理解為什麼是用這個詞,「巨」和「大」相關,但「集」呢?我想是如果能看到其真正編譯時的程式碼,可能是數萬行的程式碼,原因是所看到定義的Marco其實不是單數,而是Marco"s", 一個看起來簡單的定義,背後必包含先前的定義,像是洋䓤一樣,用一層一層的定義包含起來。由這個觀點來看,「巨大的集合」的「巨集」就有了道理了。 有些詞的翻譯就比較精準,如copy 翻為「拷貝」,其實「拷備」可能更精準一點,但「貝」「備」同音,為了和原音接近,也還可以接受。當然,「複制」是最準確的。 在程式上,定義Macros的意思比較接近「置換」、「替換」、「轉換」的意思。例如 「機車」=「討人厭」而言,說某某很「機車」的意思,就是某某很「討人厭」。如果寫成「機車很漂亮」,則電腦會轉換成「討人厭很漂亮」。在MetaPost中,可以這樣定義「機車」: def 機車 = 討人厭 enddef; 後面的程式碼就會把所有機車的詞都替換成討人厭的詞。 MetaPost是由「宏」所組成的語言,它的許多內容,都是由各種「宏」組成的。例如upto就是一個宏,它的內容是: def upto = step 1 until enddef; 所以宏的定義,就是: def 宏名稱 = 替換的詞句 enddef; 所以 for i=1 upto 10: 就等於是 for i=1 step 1 until 10: 宏本身也可以傳值,而值的種類,有expr, text, suffix三種,例如 def rotatedaround (expr z, d) = %在z點週圍旋轉 shifted -z rotated d shifted z enddef; 這樣一來,z 和 d就會被傳入的值取代, rotatedaround(p+q, a+b); 就會變成 shifted -(p+q) rotated (a+b) shifted (p+q); 其中替換詞的的z會被p+q取代,而d被a+b取代。 def 宏名稱(變值類型 變值詞)=替換詞句內含變值詞句 enddef; 變值類型有expr, text, suffix三,expr是傳入變數的值,所以上例的rotatedaround(a,b)例子中,它僅傳入a, b分別代表的值,如a=(0,1); b=2;等等。 text是內容是什麼,則傳入什麼,所以是算式也好,變數也好,寫入什麼就把全部都傳入宏中。通常是單獨使用。 suffix則是傳入變數名稱,因為變數名稱使用前必進行宣告變數型態,所以使用它可以確保傳入的變數形態正確。 這個例子可以看出expr和suffix的不同: def f(expr a)=a:=2; def g(suffix a)=a:=3; x:=1; show x; f(x); show x; g(x); show x; 其中 f(x)就會出現錯誤,因為1:=2;不可能成立。 群組與區域變數(Grouping and Local Variables) MetaPost所有的變數都是全域變數,只要一宣告使用,之後的所有程式碼都可以使用。而數個基本語句組合起來,如果最後能形成一個運算結果,這些語句可以形成一個群組(group)。形成群組(grouping)的語法是begingroup ... endgroup,在群組內的變數,如果沒有用save宣告,它們就會是全域變數,而使用save宣告的變數,就會是區域變數,其有效範圍在群組內。例如 a:=1; b:=2; begingroup c:=3; save a; a:=4; b:=a+1; show (a,b,c); endgroup; show (a,b,c); end; 結果會是(4,5,3)和(1,5,3)。a在群組中使用save,等於是在群組中宣告了a變數,所以群組裡面的a值是4,在群組外的a值仍是1。b, c二變數在群組中被改變,它們是全域變數,所以在群組外面也受到改變。 MetaPost裡面定義了hide這個宏,內容是: def hide(text t)=exitif numeric begingroup t; endgroup; enddef; hide的內容是一個判斷式(exitif),就是檢查t是否為數值,由於它是以text方式傳入,幾乎不可能成立,判斷式運算結果必為否,所程式不會中斷。但它的內容不影響其他的程式碼,而對全域變數做了運算使其改變。這用在程式中,用來計算不能直接賦值的變數型態如pair很有用。如本例是一種繪圖玩具,用二種圓所組成,小圓在大圓內圈滾動,小圓上的某一點所形成的軌跡。path內的各點用--連結中間不能有終止(;),所以使用hide來運算全域變數,而不影響path的運算。 宏也可以像是函數(founction)和子程式(sobroutine)一樣,可以寫成 def 宏名稱 = 替換詞;回傳詞 enddef; vardef是和def很像,通常如同def的寫法,如 vardef (macroname) ... = ... enddef;其內容相似於 def (macroname)... = begingroup ... endgroup enddef; 但它主要是以變數名稱為主的宏,變數名稱組成是: variable 由tag和suffix組成; suffix可以empty or suffix subscript or suffix tag 組成 subscript 由 number or [numeric expression] 組成 其中除了宏名稱 如 draw 或著運算元符號 + 等不能用在變數名稱上面,其他如 alpha, ==>, @&#$&, ~~ 都是合法的變數名稱。沒有特別意義的字母、符號,稱為(tags)。 而a[]. a.bcde這種[], .bcde的部分稱為suffix。 vardef可以對suffix的部分進行設計運算,形成新的宏。但變數名的後綴(suffix),有數字或文字。數字的部分,如同陣列,a[],意思為a[1],...a[n]或a1...a(n);文字的部分就是a.bot這類的組成。在vardef的設定中,有符號#@,@來表示陣列suffix的區別: vardef a[]b(expr p) = p shifted (#@, b) enddef; vardef ab[](expr p) = p shifted (#@, b) enddef; 二者的#@, @是有差別的。可以從這個例子來看其差異。 vardef a[]b(text t)= show (#@,@); enddef; vardef a.b[](text t)= show (#@,@); enddef; a3b(); a.b4(); end; 執行結果是 (a3,b) (a.b,4) 變數為a[]b的例子中,#@的意思是[]前面的所有的東西,包括[]在內,而@則表示[]之後的東西,本例中就是b了。[]如果在最後方,如a.b[], 則#@是[]前方所有的東西a.b,不包括[],而@則是[]的部分。如果是a.b[4],那麼@的意思為[4]而不是4。@+1是不合法的。 變數suffix是文字的部分,則使用 vardef z@# = (x@#, y@#) enddef; 其中如果是z.a1,@#的值就是a1。 宏與運算元的差異 一元運算元 它們的差別在使用的方式。以 round為例,round a; 是運算元;而round(a)是宏。實驗一下不同的使用方式: def f(expr t) = t +1 enddef; def g expr t = t+1 enddef; x:=1; show f(x); show f x; show g(x); show g x; end; 其中,f x會出現錯誤,f不能當成運算元使用,而g(x), g x 都可以執行。但這樣的運算結果正確嗎?是我們要的結果嗎?再看另一個實驗: def f expr t = t =t*2 endfor; def g primary t = t*2 endfor; x:=2; show f x +3; show g x +3; end; 二者結果不同,f x +3 結果是 10, 而g x +3 是 7。原因在MetaPost解析文件時,其運算元的先後順序是後atom, primary, secondary, tertiary, expression的層級進行,在f中,它是屬於expression級的運算元,而+是 secondary級的運算元。所以 f x +3,x+3先做運算,然後才算f 5,而得到10的結果;而g是 primary的運算元,所以它先算g x,然後才算 4+3而得到7。 一元運算元的例子: vardef incr suffix $ = $:=$+1; $ enddef; univector 是傳回長度為1的pair值: vardef univector primary z = z/abs z enddef; 二元運算元,則由primarydef, secondarydef, tertiarydef來定義其運算層級,這裡沒有 def 或 vardef的版本。如二向量的點積: primarydef a dotprod b = xpart a * xpart b + ypart a* ypart b enddef; 最後是of 運算元的例子,如 direction of 的定義是: vardef direction expr t of p = postcontrol t of p - precontrol t of p enddef; MetaPost 的 宏和運算元都可以重新定義,消除原先的含義,提供給使用者很大的彈性。 |
|
( 興趣嗜好|電腦3C ) |