HomeMapIndexSearchNewsArchivesLinksAbout LF
[Top bar]
[Bottom bar]
[Photo of the Author]
Emre Demiralp

作者介绍:

我是伊斯坦布尔美洲罗伯特学院的一名学生,同时也是伊斯坦布尔理工大学科学艺术系计算机实验室的管理员之一。这些实验室的主流操作系统是LINUX。

兴趣: PovRay,PostScript,动画,CD设计,编程,全息摄影,等等。1994年起成为LINUX用户。

目录:

  1. 引言
  2. 栈操作符
  3. 数学运算符

PostScript之二-操作数栈,栈操作符和数学运算符

[Ilustration]

摘要:

不积小流,无以成江海。
-土耳其格言-
作者描述了PostScript语言的操作数栈,介绍了栈操作和数学运算符。本文并不是对操作数栈的全面阐述,这将在后续章节中继续。



引言:

这是第二篇关于 PostScript 的系列文章。本文的主要目的是论述堆栈的操作。操作数栈可能是 PostScript 中最主要的部分。赋值、算术或数学运算、循环和逻辑运算都在这块特殊的存储区内进行。是的!堆栈是一块特殊的存储区,被 PostScript 用来执行几乎所有我们要求它完成的操作。堆栈以后进先出的规则保持信息。你可以把它形象化成一根一端封闭的导管。当你向它里面放入某物时,它会将管内所有的东西都推向封闭的一端以释放出空间。因此,最后进入的元素总是离开口的那一端最近。堆栈内的元素可以是字符串、数字常量、键码、数据块……   

栈操作符

虽然堆栈内的元素已经排列有序,但同时还存在着一些允许我们重新对这些元素进行排列的栈操作符。这些操作应用于堆栈内的一个或多个元素。操作符按其定义操纵堆栈内的元素。它们是否需要参数(在 PostScript 的术语中被称为操作数)依所进行的操作而定。若需要参数,则它们首先必须被推入堆栈中。之后,操作符依据这些参数采取相应的动作。这里我们给出了这些操作符的一个列表,并有详细的说明。我们还在后面给出了一些例子以阐明更多的细节。 

pop: 此操作符删除操作数栈顶(最后进入)的元素。

exch: 此操作符对换操作数栈顶的两个元素。

dup: 此操作符复制最后进入操作数栈的元素并将其推入操作数栈,即复制栈顶元素。 

copy: 此操作符需要一个在执行前被推入操作数栈的整型操作数(即参数)。设此整型参数为n,则给出命令 n copy 。此操作完成后,位于栈顶的n个元素的拷贝作为一批最后进入的元素而被置入操作数栈中。换言之,copy 是一个批复制操作符。 

index: 此操作符需要一个在执行前被推入操作数栈的整型操作数。设此整型参数为n,则给出命令 n index 。操作完成后,栈顶第n个元素的一个拷贝作为最后进入的元素被置入操作数栈。换言之,index 操作符能选中操作数栈的一个内部元素,生成它的拷贝,并置入操作数栈内。元素的索引以栈顶元素为零开始。 

roll: 此操作符需要两个在执行前被推入操作数栈的整型参数。设它们分别为m和n,则给出命令 m n index 。这里m指明加入滚转操作(roll)的元素数,n指明滚转操作的次数。一次滚转操作定义为:操作数栈顶元素变为栈内的第m个元素,而原栈顶以下的m-1个元素依次向栈顶移动一个位置。此过程只在n为1时成立。当n为2时,将进行连续的两次滚转操作。换言之,m 2 roll 等效于 m 1 roll m 1 roll 。参数n可以为负数。这时发生的操作过程与n为正数时相反。这意味着命令 m n roll m -n roll 将不会产生任何效果,即没有改变操作数栈。元素的索引以栈顶元素为零开始。

clear: 此操作符删除操作数栈内的所有元素。 

count: 此操作符计算操作数栈内的元素个数。计算结果作为一个新元素而被推入操作数栈内。如果不想要此新元素,你可以给出组合命令 count pstack pop 。这里count操作的结果在被文件操作符pstack执行时显示后,pop操作再从操作数栈中删除此新元素。  

mark: 此操作符在操作数栈内放入一个标记符(-marktype-)。这个元素可以将操作数栈内的元素划分成子集。另外两个操作符 cleartomarkcounttomark 在执行时需要在栈内搜寻此元素。若找不到,则系统给出错误信息。 

cleartomark: 此操作符删除操作数栈内从栈顶开始到第一个标记符 -marktype- 之间的所有元素,包括 -marktype- 本身。若栈内没有 -marktype- 元素,则系统给出错误信息。 

counttomark: 此操作符从操作数栈内的栈顶元素开始计数,直到遇到 -marktype- 元素为止。计数结果是一整型值,作为最后的元素被推入操作数栈。所遇到的 -marktype- 元素不加入计数值。若栈内没有 -marktype- 元素,则 PostScript 产生错误信息,不做任何操作。 

现在让我们讨论一下堆栈。如果你想看到以上操作符的执行过程,必须首先启动 PostScript 解释器。正如在第一篇文章中所提到的,LINUX 世界中使用一种公共解释器软件-- ghostscript 。在命令行中给出适当的参数,gostsript 能以不同的方式启动。通常的做法只要在 X窗口环境下键入 gs 。由于X窗口的设置问题,有时会启动不了。这时会出现一个关于无法创建简便图形控制窗口的错误信息。如果不能解决此问题,就要迫使 gostscript 使用x11驱动设备。为此,你必须键入 gs -sDEVICE=x11 。不管是带参数的还是不带参数的简单命令 gs(如果能启动的话),都会创建一个在会话期间作于显示的具有白色背景的空白窗口。由于显示方面与本文无关,我们不需要此显示窗口,可以把它去掉。为此目的,我们通过在命令行 gsgs -sDEVICE=x11 后加入参数 -dNODISPLAY 使 gostscript 解释器启动后不带显示窗口。如果这样做了,则在显示完一段版权声明之后,便会在新一行的开始出现 gostscript 的提示符GS>。这时,gostscript 可以处理用户键入的命令了。此时操作数栈是空的。 

为查看操作数栈的内容,你可以使用文件操作符-- pstack 。它之所以被称为文件操作符,是因为它将操作数栈的信息送往确省是屏幕的标准输出文件。如果在提示符后键入此命令,但没有显示任何东西,而在新一行的开始出现了提示符GS>,这正好说明操作数栈是空的。

为元素送往操作数栈,你可以在提示符后键入此元素。例如,你想送入元素1,则在提示符后键入1就可以了。之后,下一行的开始会出现新的提示符。但此时,提示符不是GS>,而是GS<1>。这种新的提示符表示出了操作数栈内元素的个数。因此,如果在你的 gostscript 会话期间出现了提示符 GS<123> ,则意味着在操作数栈内有123个元素。 

你可以在一次操作内往操作数栈输入多个元素。为此,你必须连续地键入所有的元素,并用空格分开。例如,如果你在提示符后键入1 2 3 4 5 6 ,则元素1,2,3,4,5,6都将被分别推入操作数栈。如果在这之后执行pstack操作,元素会以垂直顺序显示,并且最后进入的元素最先显示。以上两个命令行的对话过程显示如下:

GS>1 2 3 4 5 6 
GS<6>pstack
6
5
4
3
2
1
GS<6>
在一次单步操作内实现元素的输入并显示堆栈内容也是可能的。所有需要做的只是在将被输入的元素后面加上pstack命令,即:
GS>1 2 3 4 5 6 pstack
6
5
4
3
2
1
GS<6>
到现在为止,我们输入的元素都是数字。数字不是所唯一允许的元素类型,可以输入其它类型的元素如变量或键码、字符串、数据块等。我们会在后面详细论及。然而,现在我们必须说明:如果你想输入如单个字符a或字符串abc时,会出现错误信息。这是因为 PostScript 无法理解这些东西。如果你想输入字符或字符串,必须将它们用()包围起来。这里我们提及一个被称为 marktype 的特殊类型的元素。为此我们给出下面的一个对话例子:
GS>1 2 3 mark 4 5 6 pstack
6
5
4
-marktype-
3
2
1
GS<7>
现在我们来看一些用于操纵操作数栈的操作符。我将给出一个对话例子来说明这些操作符是如何作用的,并结束这一小节,而不准备作更进一步的解释说明。
GS>1 2 3 mark 4 5 6 pstack
6
5
4
-marktype-
3
2
1
GS<7>pop pstack
5
4
-marktype
3 
2
1
GS<6>exch pstack
4
5
-marktype
3
2
1
GS<6>dup pstack
4
4
5
-marktype-
3
2
1
GS<7>2 copy pstack
4
4
4
4
5
-marktype
3
2
1
GS<9>5 index pstack
-marktype-
4
4
4
4
5
-marktype
3
2
1
GS<10>cleartomark cleartomark pstack
3
2
1
GS<3>3 1 roll pstack
2
1
3
GS<3>count pstack
3
2
1
3
GS<4>mark 7 8 pstack
8
7
-marktype-
3
2
1
3
GS<7>counttomark pstack
2
8
7
-marktype-
3
2
1
3
GS<8>clear pstack
GS>
 

数学运算符

除了用于操纵 PostScript 操作数栈的操作符外,还有一些算术和数学运算符。下面我们给出这些运算符,但是没有给出例子。有了以上的对话例子,现在读者应该可以自己将这些例子举出来。  

add: 此命令需要两个参与加法运算的数值型参数。设这两个值为m和n,则命令表示为 m n add 。执行时,m和n先后进入操作数栈,最后一步是将操作数栈顶的两个元素相加。该操作产生一个值为m和n之和的新元素。操作结束后,m和n没有被保留在操作数栈中,相加的结果成了操作数栈的栈顶元素。

div: 此命令需要两个参与除法运算的数值型参数。设这两个值为m和n,则命令表示为 m n div 。其执行机制与add相同。除法操作在浮点数的算术基础上进行。操作结束后,结果作为新元素留在操作数栈内,不保留m和n。

idiv: 此命令需要两个参与整数除法运算的数值型参数。设这两个值为m和n,则命令表示为 m n idiv 。除了该除法操作的算术运算基础之外,其它执行机制都与 div 相同。这是一种整数算术除法运算。如果参数不是整数,它也会执行下去。

mod: 此命令需要两个整型参数。它计算出第一个参数除以第二个参数后所得的余数。若参数中有一个不是整数,则执行失败。执行结束后,所得的结果作为新元素留在操作数栈内。

mul: 同于 add ,div 。它是一个需要两个数值型参数的二进制操作符。所得结果是参数的积,并且作为一个新元素被保留在操作数栈中。

sub: 同于 add ,div ,mul 。唯一不同之处在于操作类型。它从第一个参数中减去第二个参数的值。参数和结果都是数值型的。执行完后,结果被保留在操作数栈内。 

exp: 这是一个二元数学运算符,需要两个参数。第一个是基数,第二个是指数。它执行指数运算。参数的值必须在幂函数运算所允许的范围内。所得结果是一浮点数,作为新元素被保留在操作数栈内。 

atan: 这是另一个用于求角度值的二元数学运算符。所得角度值在0度和360度之间。它需要两个参数。第一个参数和第二个参数之比代表所求角度的正切值。任何一个参数都可以为零,但不能都为零。参数的符号决定了所得结果所在象限。第一个参数值为正代表正y轴象限,相应地,第二个参数为正代表正x轴象限。 

abs: 这是一个一元数学运算符,只需要一个参数,所得结果是它的绝对值。同上,此结果作为一个新元素被保留在操作数栈中。

neg: 改变它仅有的参数的符号。这是一个一元算术运算符。 

ceiling: 这是一个一元运算符,对参数向上取整。

floor: 这是一个一元运算符,对参数向下取整。

round: 这是一个一元运算符,取离参数最近的整数。

truncate: 这是一个一元运算符,去掉参数的小数部分。 

sqrt: 这是一个一元运算符,计算参数的平方根。

cos: 这是一个一元运算符,计算参数的余弦值。参数以角度值的形式给出。

sin: 这是一个一元运算符,计算参数的正弦值。参数以角度值的形式给出。

ln: 这是一个一元运算符,计算参数的自然对数值。

log: 这是一个一元运算符,计算参数的以10为底的对数值。 

在结束本文之前,有几点需要指出。虽然我们在以上已有所提及,可能是间接的,即命令的参数(在 PostScript 的术语中被称为操作数)可能会产生一些令人麻烦的问题。命令(在 PostScript 的术语中被称为操作符)在操作数栈内寻求它的参数。如果找到,它们被命令所用,并且被从操作数栈中删除。因此,有意或无意地给出一个在操作数栈中不存在它所需要的参数值的命令,轻则产生关于栈顶元素类型与所需参数类型不匹配的错误信息,重则会删掉栈顶的一些元素。用户必须注意到这一点。 

在结束之前,我们建议想要使用 PostScript 的用户去写一些更加复杂和易懂的程序。在本系列以后的篇章中,将介绍更多的关于 PostScript 语言的细节。我们乐于接受关于本文的任何问题和建议。

由 Jorge M.Paulo 和Jose Quesada审议


主业由LinuxFocus编辑小组维护
© Emre Demiralp
LinuxFocus 1999