主頁 地圖 索引 搜尋 新聞 前期雜誌 相關聯結 關於 LinuxFocus
[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