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:

Array structure
[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.2.2. Indexed Access

Array elements are accessed by zero-based index:

Array indexing
[10 20 30 40 50] 0 get      % Returns 10 (first element)
[10 20 30 40 50] 2 get      % Returns 30 (third element)
[10 20 30 40 50] 4 get      % Returns 50 (last element)

1.2.3. Mutable

Array contents can be modified after creation:

Modifying arrays
[1 2 3] dup 0 99 put        % Changes first element to 99
                            % Result: [99 2 3]

1.3. Array Creation

1.3.1. Literal Array Syntax

The most common way to create arrays uses square brackets [ ]:

Literal array creation
[ ]                 % 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
Bracket execution
[                   % 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:

Creating with array operator
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

Constructing arrays with operators
% 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

array index → any

Get element at index

put

array index any → -

Set element at index

getinterval

array index count → subarray

Extract subarray

putinterval

array1 index array2 → -

Replace subarray

length

array → int

Get array length

Element access examples
% 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

aload

array → any₁…​anyₙ array

Push all elements

astore

any₁…​anyₙ array → array

Pop elements into array

copy

array₁ array₂ → subarray₂

Copy elements

forall

array proc → -

Iterate over elements

Manipulation examples
% 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:

Regular array characteristics
% 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:

Executable arrays
% 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.5.3. Packed Arrays (Level 2+)

Packed arrays are space-efficient read-only arrays:

Packed array creation
% Using packedarray operator
mark 1 2 3 4 5 ] packedarray
% or
mark 1 2 3 4 5 counttomark packedarray

% Type is packedarraytype
mark 1 2 3 ] packedarray type   % Returns /packedarraytype

1.5.4. Array Type Comparison

Aspect Regular Array Executable Array Packed Array

Creation

[ ] or array

{ }

packedarray

Mutability

Mutable

Mutable

Read-only

Executable

No (by default)

Yes

Varies

Type

arraytype

arraytype

packedarraytype

Space

Normal

Normal

Compact

Access

Fast

Fast

Slower

1.6. Multi-Dimensional Arrays

Arrays can contain other arrays to create multi-dimensional structures.

1.6.1. Two-Dimensional Arrays

Matrix representation
% 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

Building nested 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

Working with nested arrays
% 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

Converting 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

Unpacking arrays
% 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

Deep vs shallow copy
% 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

Reversing array elements
/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

Joining arrays
/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

Standard iteration
% 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

Index-based iteration
/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

Transform array elements
/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

Select matching elements
/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

Safe array access
/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.9.2. Last Element

Getting last element
/Last {
    % in: array
    % out: last-element

    dup length 1 sub get
} def

[1 2 3 4 5] Last               % Returns 5

1.9.3. First Element

Getting first element
/First {
    % in: array
    % out: first-element

    0 get
} def

[1 2 3 4 5] First              % Returns 1

1.10. Array as Stack

Arrays can simulate stack operations.

1.10.1. Push Operation

Array push
/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.10.2. Pop Operation

Array pop
/ArrayPop {
    % in: array
    % out: remaining-array last-element

    dup length 0 eq {
        (Empty array) print null
    } {
        dup dup length 1 sub get    % Get last
        exch                         % last array
        0 1 index length 1 sub getinterval  % Remaining
        exch
    } ifelse
} def

[1 2 3 4] ArrayPop             % Returns [1 2 3] 4

1.11. Performance Considerations

1.11.1. Array Size

Pre-allocate when possible
% 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.11.2. Array Copying

Minimize copies
% SLOW - unnecessary copy
[1 2 3] dup length array copy
dup 0 99 put

% FAST - modify in place if possible
[1 2 3] dup 0 99 put

1.11.3. Packed Arrays

Use for read-only data (Level 2+)
% Space-efficient for constants
/Constants mark
    3.14159                     % pi
    2.71828                     % e
    1.41421                     % sqrt(2)
counttomark packedarray def

% Read-only, compact storage
Constants 0 get                 % Get pi

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.12.2. Initialization

Initialize arrays properly
% GOOD - clear initialization
5 array
0 1 4 { 2 copy 0 put } for

% BETTER - all at once
[0 0 0 0 0]

% BEST - if all same value
5 { 0 } repeat 5 array astore

1.12.3. Access Patterns

Efficient access
% GOOD - single access
array 5 get

% BAD - repeated access
array 5 get
array 5 get
array 5 get

% BETTER - cache in variable
/val array 5 get def
val val val

1.13. Common Pitfalls

1.13.1. Shared References

Arrays store 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

Index errors
% 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

Iterator invalidation
% 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

Simple bubble sort
/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
Linear 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


Back to top

Copyright © 2025 Ribose. PostScript is a trademark of Adobe. Distributed under the MIT License.