当前位置: > www.tlc188.com >

MetaPost语言?宏(Macros)

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,同乐城tlc88.com, text, suffix三,expr是传入变数的值,所以上例的rotatedaround(a,b)例子中,它仅传入a, b分辨代表的值,如a=(0,1); b=2;等等。

text是内容是什么,则传入什么,所以是算式也好,同乐城tlc88.com,变数也好,写入什么就把全部都传入宏中。通常是独自使用。

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这个宏,同乐城tlc88.com,内容是:

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的例子中,#@的意思是[]前面的所有的?鳎???]在内,而@则表示[]之后的?鳎?纠?芯褪?了。[]如果在最后方,如a.b[], 则#@是[]前方所有的??.b,不包含[],而@则是[]的部分。如果是a.b[4],那么@的意思为[4]而不是4。@+1是不合法的。

变数suffix是文字的部分,则使用@#来表示其suffix的部分,如MetaPost中,z的定义:

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 的 宏跟运算元都可能从新定义,消除原来的含意,供应给利用者很大的弹性。