- 1. Arrays
- 1.1. Overview
- 1.2. Array Characteristics
- 1.3. Array Creation
- 1.4. Array Operations
- 1.5. Array Types
- 1.6. Multi-Dimensional Arrays
- 1.7. Array Patterns and Idioms
- 1.8. Array Iteration
- 1.9. Array Access Patterns
- 1.10. Array as Stack
- 1.11. Performance Considerations
- 1.12. Best Practices
- 1.13. Common Pitfalls
- 1.14. Array Examples
- 1.15. See Also
1. Arrays
Arrays are ordered collections of PostScript objects. They provide indexed access to elements and are fundamental data structures for storing sequences of values, building complex data structures, and implementing procedures.
1.1. Overview
An array is a composite object that contains a sequence of elements. Each element can be any PostScript object, including other arrays. Arrays are one of the most versatile data structures in PostScript.
1.2. Array Characteristics
1.2.1. Composite Object
Arrays are composite objects that store references to other objects:
[1 2 3] % Array of three integers
[(a) (b) (c)] % Array of three strings
[1 (two) /three] % Array of mixed types
[[1 2] [3 4]] % Array of arrays (nested)
1.3. Array Creation
1.3.1. Literal Array Syntax
The most common way to create arrays uses square brackets [ ]:
[ ] % Empty array
[1] % Single element
[1 2 3] % Multiple elements
[1 2 add 3] % Contains result of 2+1, i.e., [3 3]
1.3.2. How [ ] Work
Square brackets execute immediately during token scanning:
% When [ is encountered:
% 1. Push mark onto operand stack
% 2. Continue scanning normally
% When ] is encountered:
% 1. Count elements from mark to top
% 2. Create array with those elements
% 3. Pop elements and mark
% 4. Push new array
[ % Pushes mark, stack: [mark]
1 % Pushes 1, stack: [mark 1]
2 % Pushes 2, stack: [mark 1 2]
add % Executes: stack: [mark 3]
] % Creates [3], stack: [[3]]
% Compare with procedures:
{ % Pushes mark, enters proc mode
1 2 add % Does NOT execute (deferred)
} % Creates {1 2 add}, executable array
1.3.3. Array Operator
The array operator creates an empty array of specified size:
5 array % Creates [null null null null null]
0 array % Creates [ ] (empty array)
% Fill array manually
5 array
dup 0 10 put
dup 1 20 put
dup 2 30 put
dup 3 40 put
dup 4 50 put % Result: [10 20 30 40 50]
1.3.4. Building Arrays Programmatically
% Using mark and array operations
mark 1 2 3 4 5 counttomark array astore
% Result: [1 2 3 4 5]
% Using astore
5 array % Create empty array
1 2 3 4 5 % Push elements
5 -1 roll % Bring array to top
astore % Store elements: [1 2 3 4 5]
1.4. Array Operations
1.4.1. Accessing Elements
| Operator | Stack Effect | Description |
|---|---|---|
|
|
Get element at index |
|
|
Set element at index |
|
|
Extract subarray |
|
|
Replace subarray |
|
|
Get array length |
% get
[10 20 30] 1 get % Returns 20
% put
[1 2 3] dup 1 99 put % Modifies to [1 99 3]
% getinterval
[10 20 30 40 50] 1 3 getinterval % Returns [20 30 40]
% putinterval
[1 2 3 4 5] dup 1 [88 99] putinterval
% Result: [1 88 99 4 5]
% length
[1 2 3 4 5] length % Returns 5
1.4.2. Array Manipulation
| Operator | Stack Effect | Description |
|---|---|---|
|
|
Push all elements |
|
|
Pop elements into array |
|
|
Copy elements |
|
|
Iterate over elements |
% aload - unpack array onto stack
[1 2 3] aload % Stack: [1 2 3 [1 2 3]]
pop % Remove array ref: [1 2 3]
% astore - pack stack into array
3 array % Create empty array
1 2 3 % Push values
3 -1 roll % Get array
astore % Store: [1 2 3]
% copy - duplicate array contents
[1 2 3] dup length array copy % Creates new [1 2 3]
% forall - iterate
[10 20 30] { 2 mul = } forall % Prints 20, 40, 60
1.5. Array Types
1.5.1. Regular Arrays
Standard arrays created with [ ] or array:
% Mutable
[1 2 3] dup 0 99 put % OK
% Can contain any objects
[1 (two) /three { four } [5]] % Mixed types OK
% Type is arraytype
[1 2 3] type % Returns /arraytype
1.5.2. Executable Arrays (Procedures)
Arrays with the executable attribute are procedures:
% Created with { }
{ 1 2 add } % Executable array
% Or by converting
[1 2 /add] cvx % Make executable
% Type is still arraytype
{ 1 2 add } type % Returns /arraytype
{ 1 2 add } xcheck % Returns true
1.6. Multi-Dimensional Arrays
Arrays can contain other arrays to create multi-dimensional structures.
1.6.1. Two-Dimensional Arrays
% 2x3 matrix
[
[1 2 3]
[4 5 6]
]
% Access element: row 1, column 2
dup 1 get 2 get % Returns 6
% Create 3x3 identity matrix
[
[1 0 0]
[0 1 0]
[0 0 1]
]
1.6.2. Creating Multi-Dimensional Arrays
% Create 2x3 array of zeros
2 array % Outer array
dup 0 3 array put % First row
dup 1 3 array put % Second row
% Result: [[null null null] [null null null]]
% Fill with values
dup 0 get 0 1 put % [0][0] = 1
dup 0 get 1 2 put % [0][1] = 2
% ... etc
1.6.3. Nested Array Operations
% Define helper procedures
/GetMatrix {
% in: matrix row col
% out: value
3 -1 roll exch get exch get
} def
/SetMatrix {
% in: matrix row col value
4 -1 roll dup % matrix row col value matrix matrix
4 -1 roll get % matrix col value matrix row-array
3 -1 roll % matrix value matrix row-array col
3 -1 roll put % matrix
} def
% Usage
[[1 2][3 4]] 1 1 GetMatrix % Returns 4
[[1 2][3 4]] 0 1 99 SetMatrix % Sets [0][1] to 99
1.7. Array Patterns and Idioms
1.7.1. Stack to Array
% Method 1: Using mark
mark 1 2 3 4 5 counttomark array astore
% Method 2: Count first
% If you know count is 5
5 array astore % Pops 5 items into array
1.7.2. Array to Stack
% Unpack all elements
[1 2 3 4 5] aload pop % Stack: [1 2 3 4 5]
% Unpack to separate variables
[1 2 3] aload /c exch def /b exch def /a exch def pop
% Now: a=1, b=2, c=3
1.7.3. Array Copying
% Shallow copy - references shared
[1 [2 3] 4] dup length array copy
% Nested [2 3] is shared
% Deep copy requires recursion
/DeepCopy {
dup type /arraytype eq {
dup length array
0 1 2 index length 1 sub {
2 index 1 index get DeepCopy
2 index 3 1 roll put
} for
exch pop
} if
} def
1.7.4. Array Reversal
/Reverse {
% in: array
% out: reversed-array
dup length array % Create result array
0 1 2 index length 1 sub { % For each index
2 index % Original array
2 index % Index i
1 index length 1 sub % Length-1
exch sub % Length-1-i (reverse index)
get % Get element
2 index 3 1 roll put % Store in result
} for
exch pop
} def
[1 2 3 4 5] Reverse % Returns [5 4 3 2 1]
1.7.5. Array Concatenation
/Concat {
% in: array1 array2
% out: combined-array
2 copy % array1 array2 array1 array2
length exch length add % array1 array2 total-length
array % array1 array2 result
% Copy first array
dup 0 4 index putinterval
% Copy second array
dup 3 index length 3 index putinterval
3 1 roll pop pop
} def
[1 2 3] [4 5 6] Concat % Returns [1 2 3 4 5 6]
1.8. Array Iteration
1.8.1. Using forall
% Print each element
[10 20 30] { = } forall
% Transform elements
[1 2 3 4] {
2 mul = % Double and print each
} forall
% Accumulate sum
0 % Initial accumulator
[1 2 3 4 5] {
add % Add to accumulator
} forall % Result: 15
1.8.2. Using for Loop
/arr [10 20 30 40 50] def
% Iterate by index
0 1 arr length 1 sub {
arr exch get = % Print each element
} for
% Process pairs
0 2 arr length 1 sub {
arr exch get = % Print even-indexed elements
} for
1.8.3. Mapping
/Map {
% in: array procedure
% out: result-array
exch dup length array % proc array result-array
0 1 3 index length 1 sub { % For each index
% Stack: proc array result i
3 index 1 index get % Get element
4 index exec % Apply procedure
2 index 3 1 roll put % Store in result
} for
3 1 roll pop pop
} def
[1 2 3 4] { 2 mul } Map % Returns [2 4 6 8]
1.8.4. Filtering
/Filter {
% in: array predicate
% out: filtered-array
% First pass: count matches
0 2 index { % counter array
1 index exec { % If predicate true
1 add % Increment counter
} if
} forall
% Create result array
array % result-array
0 % index for result
3 -1 roll { % For each element
2 index exec { % If predicate true
2 index 2 index 3 -1 roll put
1 add % Next index
} {
pop % Discard element
} ifelse
} forall
pop exch pop
} def
[1 2 3 4 5 6] { 2 mod 0 eq } Filter % Returns [2 4 6]
1.9. Array Access Patterns
1.9.1. Bounds Checking
/SafeGet {
% in: array index
% out: element or null
2 copy length ge { % Index >= length?
pop pop null
} {
get
} ifelse
} def
[1 2 3] 5 SafeGet % Returns null (out of bounds)
[1 2 3] 1 SafeGet % Returns 2
1.10. Array as Stack
Arrays can simulate stack operations.
1.10.1. Push Operation
/ArrayPush {
% in: array element
% out: new-array
exch % element array
dup length 1 add array % element array new-array
% Copy old elements
dup 0 3 index putinterval
% Add new element
dup 2 index length 3 -1 roll put
exch pop
} def
[1 2 3] 4 ArrayPush % Returns [1 2 3 4]
1.11. Performance Considerations
1.11.1. Array Size
% SLOW - repeated concatenation
[ ]
1 1 100 {
% Create new array each time
exch dup length 1 add array
dup 0 3 index putinterval
dup 2 index length 3 -1 roll put
} for
% FAST - pre-allocate
100 array
0 1 99 {
2 copy 1 index 1 add put
} for
1.12. Best Practices
1.12.1. Array Design
-
Use arrays for homogeneous collections
-
Pre-allocate arrays when size is known
-
Document expected array structure
-
Validate array bounds before access
-
Use meaningful variable names for arrays
1.13. Common Pitfalls
1.13.1. Shared References
% WRONG - shared subarray
/subarr [1 2 3] def
[subarr subarr] % Both refer to same array
dup 0 get 0 99 put % Modifies both!
% RIGHT - separate copies
/subarr [1 2 3] def
[subarr dup length array copy] % Independent copies
1.13.2. Out of Bounds
% WRONG - no bounds check
[1 2 3] 10 get % rangecheck error
% RIGHT - validate first
/arr [1 2 3] def
/idx 10 def
idx 0 ge idx arr length lt and {
arr idx get
} {
(Index out of bounds) print
null
} ifelse
1.13.3. Array Modification During Iteration
% DANGEROUS - modifying during forall
[1 2 3 4] {
dup 2 eq {
% Can't safely modify array here
} if
} forall
% SAFE - collect indices, then modify
[ ]
0 [1 2 3 4] {
dup 2 eq {
2 index exch 2 index length array
% Build list of indices
} if
} forall
% Then modify based on indices
1.14. Array Examples
1.14.1. Sorting
/BubbleSort {
% in: array
% out: sorted-array (in place)
dup length 1 sub 0 exch { % Outer loop
dup 0 exch { % Inner loop
2 index dup % array i array array
2 index get % array i array elem[i]
2 index 1 add get % array i array elem[i] elem[i+1]
gt { % If elem[i] > elem[i+1]
% Swap elements
2 index 2 index get
2 index 2 index 1 add get
3 index 3 1 roll put
2 index exch 1 add exch put
} {
pop pop
} ifelse
} for
pop
} for
} def
1.14.2. Search
/IndexOf {
% in: array value
% out: index or -1
exch % value array
-1 exch % value index array
0 exch { % For each element
2 index eq { % If matches
pop exch pop exit % Return current index
} {
1 add % Next index
} ifelse
} forall
% If not found, -1 remains
} def
[10 20 30 40] 30 IndexOf % Returns 2
[10 20 30 40] 99 IndexOf % Returns -1
1.15. See Also
-
Procedures - Executable arrays
-
Objects - Composite object model
-
Strings - Similar indexed structure
-
Data Types - Array type details
-
array - Create arrays
-
aload - Unpack arrays
-
astore - Pack arrays
-
get - Access elements
-
put - Modify elements
-
forall - Iterate arrays