- 1. Arrays
1. Arrays
Arrays are ordered collections of objects in PostScript. They provide a way to group related data and are fundamental to many PostScript operations.
1.1. Overview
PostScript arrays:
-
Store an ordered sequence of any PostScript objects
-
Have a fixed size once created
-
Can contain mixed types (numbers, strings, other arrays, etc.)
-
Support both literal and executable forms
-
Use zero-based indexing
Arrays are used extensively for:
-
Coordinate lists
-
Color spaces
-
Transformation matrices
-
Dash patterns
-
Procedure definitions
1.2. Creating Arrays
1.2.1. Literal Array Syntax
The most common way to create arrays:
% Literal array notation
[1 2 3 4 5] % Array of integers
[/a /b /c] % Array of names
[(hello) (world)] % Array of strings
[1 (two) /three] % Mixed types
% Nested arrays
[1 [2 3] [4 [5 6]]] % Arrays within arrays
1.2.2. The array Operator
Creates an empty array of specified size:
% array: n -> array
5 array % Creates [null null null null null]
% Store in variable
/myArray 10 array def
1.3. Accessing Array Elements
1.3.1. The get Operator
Retrieves element by index:
% get: array index -> element
[10 20 30 40 50] 2 get % Returns 30 (index 2)
[10 20 30 40 50] 0 get % Returns 10 (index 0)
[10 20 30 40 50] 4 get % Returns 50 (index 4)
Using with variables:
/colors [
[1 0 0] % Red
[0 1 0] % Green
[0 0 1] % Blue
] def
colors 0 get aload pop setrgbcolor % Set red
colors 1 get aload pop setrgbcolor % Set green
1.3.2. The put Operator
Stores element at index:
% put: array index value -> -
/myArray [1 2 3 4 5] def
myArray 2 99 put % Changes index 2 to 99
% myArray is now [1 2 99 4 5]
Modifying nested arrays:
/matrix [
[1 0 0]
[0 1 0]
[0 0 1]
] def
% Modify element in nested array
matrix 1 get 1 99 put
% matrix is now [[1 0 0] [0 99 0] [0 0 1]]
1.4. Array Manipulation
1.4.1. The aload Operator
Pushes all array elements onto stack:
% aload: array -> element1 element2 ... elementN array
[10 20 30] aload
% Stack: 10 20 30 [10 20 30]
% Discard array reference
[10 20 30] aload pop
% Stack: 10 20 30
Common use - unpacking coordinates:
/point [100 200] def
point aload pop moveto % moveto 100 200
1.4.2. The length Operator
Returns number of elements:
% length: array -> integer
[1 2 3 4 5] length % Returns 5
[] length % Returns 0
[[1 2] [3 4]] length % Returns 2 (not 4)
Using length in loops:
/myArray [10 20 30 40 50] def
0 1 myArray length 1 sub {
/i exch def
myArray i get = % Print each element
} for
1.4.3. The forall Operator
Iterates over array elements:
% forall: array procedure -> -
[10 20 30 40 50] {
dup mul = % Square and print each element
} forall
% Prints: 100, 400, 900, 1600, 2500
With multiple operations:
/sum 0 def
[1 2 3 4 5] {
sum add /sum exch def
} forall
% sum is now 15
Processing array of points:
/points [
[100 100]
[200 100]
[200 200]
[100 200]
] def
newpath
points {
aload pop
points 0 get eq {
moveto
} {
lineto
} ifelse
} forall
closepath
stroke
1.4.4. Copying Arrays
Arrays are references, not values:
% Wrong: both reference same array
/array1 [1 2 3] def
/array2 array1 def
array2 0 99 put
% Both array1 and array2 are [99 2 3]
% Correct: create new array
/array1 [1 2 3] def
/array2 array1 length array def
array2 0 array1 putinterval
array2 0 99 put
% array1 is [1 2 3], array2 is [99 2 3]
Using getinterval to copy:
/array1 [1 2 3 4 5] def
/array2 array1 0 array1 length getinterval def
% array2 is a copy of array1
1.5. Array Patterns and Techniques
1.5.1. Pattern 1: Array as Stack
Use array indices to simulate stack operations:
/Stack 100 array def
/StackPtr 0 def
/push {
Stack StackPtr 3 -1 roll put
/StackPtr StackPtr 1 add def
} def
/pop {
/StackPtr StackPtr 1 sub def
Stack StackPtr get
} def
% Usage
42 push
99 push
pop = % Prints 99
pop = % Prints 42
1.5.2. Pattern 2: Array as Lookup Table
% Day of week lookup
/days [
(Sunday)
(Monday)
(Tuesday)
(Wednesday)
(Thursday)
(Friday)
(Saturday)
] def
% Get day name
3 days exch get = % Prints "Wednesday"
Color palette:
/palette [
[1 0 0] % Red
[0 1 0] % Green
[0 0 1] % Blue
[1 1 0] % Yellow
[1 0 1] % Magenta
[0 1 1] % Cyan
] def
/setColor { % index -> -
palette exch get
aload pop setrgbcolor
} def
2 setColor % Set to blue
1.5.3. Pattern 3: Coordinate Arrays
% Path from coordinate array
/drawPath { % array -> -
1 dict begin
/coords exch def
newpath
coords {
aload pop
coords 0 get eq { moveto } { lineto } ifelse
} forall
end
} def
% Usage
[
[100 100]
[200 100]
[200 200]
[100 200]
] drawPath
closepath
stroke
1.6. Special Array Types
1.6.1. Packed Arrays
More memory-efficient, read-only arrays:
% packedarray: mark obj1 obj2 ... objN packedarray -> array
% Or use << >> syntax (Level 2+)
mark 1 2 3 4 5 packedarray
% Creates read-only array [1 2 3 4 5]
% Shorter syntax
<< 1 2 3 4 5 >> % Packed array (Level 2+)
Properties:
-
Cannot be modified with
put -
Use less memory
-
Faster access
-
Often used for procedure bodies
1.6.2. Transformation Matrices
Special 6-element arrays:
% Matrix: [a b c d tx ty]
% Default identity matrix
[1 0 0 1 0 0]
% Scale matrix (2x, 3y)
[2 0 0 3 0 0]
% Translation matrix (+100, +200)
[1 0 0 1 100 200]
% Rotation matrix (45 degrees)
45 cos 45 sin 45 sin neg 45 cos 0 0
6 array astore
Using matrix arrays:
% Create and apply transformation
/myMatrix [2 0 0 2 100 100] def
myMatrix concat % Apply transformation
% Or use matrix operators
matrix currentmatrix % Get current matrix
1.7. Practical Array Examples
1.7.1. Example 1: Bar Chart Data
/barChart { % array x y barWidth spacing -> -
6 dict begin
/spacing exch def
/barWidth exch def
/y exch def
/x exch def
/data exch def
0 1 data length 1 sub {
/i exch def
% Draw bar
x i barWidth spacing add mul add
y
barWidth
data i get
rectfill
} for
end
} def
% Usage
[50 80 120 90 110] 100 100 20 5 barChart
1.7.2. Example 2: Scatter Plot
/scatterPlot { % pointArray -> -
{
aload pop
newpath
2 copy 3 0 360 arc
fill
} forall
} def
% Usage
[
[100 150]
[150 200]
[200 180]
[250 220]
[300 190]
] scatterPlot
1.7.3. Example 3: Color Gradient
/gradientColors { % startColor endColor steps -> colorArray
3 dict begin
/steps exch def
/ec exch def
/sc exch def
[
0 1 steps 1 sub {
/i exch def
/t i steps 1 sub div def
[
sc 0 get 1 t sub mul ec 0 get t mul add
sc 1 get 1 t sub mul ec 1 get t mul add
sc 2 get 1 t sub mul ec 2 get t mul add
]
} for
]
end
} def
% Create gradient from red to blue
[1 0 0] [0 0 1] 10 gradientColors
% Returns array of 10 RGB colors
1.7.4. Example 4: Data Sorting (Bubble Sort)
/bubbleSort { % array -> sortedArray
1 dict begin
/arr exch def
0 1 arr length 2 sub {
/i exch def
0 1 arr length 2 sub i sub {
/j exch def
arr j get arr j 1 add get gt {
% Swap
/temp arr j get def
arr j arr j 1 add get put
arr j 1 add temp put
} if
} for
} for
arr
end
} def
% Usage
[5 2 8 1 9 3] bubbleSort
% Returns [1 2 3 5 8 9]
1.7.5. Example 5: Array Filtering
/filter { % array procedure -> filteredArray
2 dict begin
/proc exch def
/arr exch def
[
arr {
dup proc exec {
% Include in result
} {
pop % Exclude from result
} ifelse
} forall
]
end
} def
% Usage - filter even numbers
[1 2 3 4 5 6 7 8 9 10] {
2 mod 0 eq
} filter
% Returns [2 4 6 8 10]
1.8. Advanced Array Techniques
1.8.1. Multi-dimensional Arrays
% 2D array (3x3)
/grid [
[1 2 3]
[4 5 6]
[7 8 9]
] def
% Access element grid[row][col]
/getCell { % row col -> value
exch grid exch get exch get
} def
% Set element grid[row][col] = value
/setCell { % row col value -> -
3 1 roll
grid 3 1 roll get
3 1 roll put
} def
% Usage
1 2 getCell = % Gets grid[1][2] = 6
0 1 99 setCell % Sets grid[0][1] = 99
1.8.2. Sparse Arrays
Use dictionaries for sparse arrays:
% Sparse array using dictionary
/sparseArray 100 dict def
% Set value at index
/sparseSet { % index value -> -
sparseArray 3 1 roll put
} def
% Get value at index (0 if not set)
/sparseGet { % index -> value
sparseArray exch 2 copy known {
get
} {
pop pop 0
} ifelse
} def
% Usage
100 42 sparseSet
5000 99 sparseSet
100 sparseGet = % Returns 42
200 sparseGet = % Returns 0 (not set)
1.8.3. Array Mapping
/map { % array procedure -> newArray
2 dict begin
/proc exch def
/arr exch def
[
arr {
proc exec
} forall
]
end
} def
% Usage - square all elements
[1 2 3 4 5] {
dup mul
} map
% Returns [1 4 9 16 25]
1.8.4. Array Reduction
/reduce { % array initial procedure -> result
3 dict begin
/proc exch def
/accum exch def
/arr exch def
arr {
accum exch proc exec
/accum exch def
} forall
accum
end
} def
% Usage - sum all elements
[1 2 3 4 5] 0 { add } reduce
% Returns 15
% Product
[1 2 3 4 5] 1 { mul } reduce
% Returns 120
1.9. Best Practices
1.9.1. Pre-allocate Arrays When Possible
% Good: pre-allocated
/results 100 array def
0 1 99 {
/i exch def
results i i dup mul put
} for
% Less efficient: building dynamically
/results [] def
0 1 99 {
dup mul
% Would need to copy and extend array
} for
1.9.2. Use Appropriate Data Structures
% Good: dictionary for named access
/config << /width 100 /height 200 >> def
config /width get
% Bad: array with magic indices
/config [100 200] def
config 0 get % What does index 0 mean?
1.10. Common Pitfalls
1.10.1. Array Reference vs. Copy
% Wrong: modifying shared reference
/a [1 2 3] def
/b a def
b 0 99 put
% Both a and b are [99 2 3]!
% Correct: create copy
/a [1 2 3] def
/b a 0 a length getinterval def
b 0 99 put
% a is [1 2 3], b is [99 2 3]
1.11. Performance Considerations
1.11.1. Use Packed Arrays for Read-Only Data
% Faster and more memory-efficient
/constants << 3.14159 2.71828 1.41421 >> def
% vs. regular array
/constants [3.14159 2.71828 1.41421] def
1.12. See Also
-
Array Syntax - Array syntax details
-
Procedures - Using arrays in procedures
-
Composite Objects - Complex data structures
-
array - Create array
-
aload - Unpack array
-
astore - Pack array
-
get - Get element
-
put - Set element
-
forall - Iterate array