Lists and Property Lists
Linear lists and property lists - creation, access, mutation, sorting, reference semantics, and the getProp vs getaProp trap.
Two list flavors
linear = [10, 20, 30] -- linear list
props = [#name: "Bob", #age: 42] -- property list (ordered key/value)
emptyL = []
emptyP = [:] -- note the colonBoth are 1-based, ordered, heterogeneous, and passed by reference.
Access
linear[2] -- 20
getAt(linear, 2) -- 20 (verbose equivalent)
linear[2] = 25 -- replace (position must exist)
setAt(linear, 5, 99) -- extends a LINEAR list with placeholder 0s if needed
props[#name] -- "Bob" (bracket by key)
props["name"] -- "Bob" (string keys coerce to match symbols)
props[1] -- "Bob" (bracket by POSITION also works on propLists)
props.name -- "Bob" (dot access, D7+)
getProp(props, #age) -- 42, ERROR if the key is missing
getaProp(props, #age) -- 42, VOID if the key is missing
getPropAt(props, 1) -- #name (key at position)getProp vs getaProp is the classic list trap: getProp raises a script error on a missing key, getaProp quietly returns VOID. Old code deliberately uses getaProp + voidP() as "has key?". On linear lists, getaProp(list, n) behaves like a safe positional get, and getProp is invalid.
Mutation
| Operation | Linear list | Property list |
|---|---|---|
| Append | add(l, v) / l.append(v) | addProp(l, #k, v) |
| Insert at position | addAt(l, pos, v) | setaProp (by key) |
| Sorted insert | add(l, v) after sort(l) keeps order | addProp keeps sort by key after sort(l) |
| Replace | setAt(l, pos, v) / l[pos] = v | setaProp(l, #k, v) (adds if missing); setProp(l, #k, v) (errors if missing) |
| Delete by position | deleteAt(l, pos) | deleteAt(l, pos) |
| Delete by value/key | deleteOne(l, v) (first match; returns nothing, check via getOne first) | deleteProp(l, #k) |
| Empty the list | deleteAll(l) (undocumented in some versions but present) or reassign [] | same |
add linear, 40 -- [10, 25, 30, 40] command form
linear.add(40) -- dot form
addAt(linear, 1, 5) -- [5, 10, 25, 30, 40]
deleteAt(linear, 2) -- [5, 25, 30, 40]
addProp(props, #city, "NYC")
setaProp(props, #age, 43) -- replace or add
deleteProp(props, #city)Duplicate keys are legal. addProp appends even if the key already exists, so a property list can hold the same key twice; getaProp/getProp then find the first occurrence, and deleteProp deletes the first. Well-behaved code uses setaProp to avoid duplicates, but an emulator must reproduce the duplicate-tolerant behavior because real content depends on it.
Search and aggregate
count(l) / l.count -- length ("the count of l" also works)
getOne(l, v) -- position of first value match, 0 if absent
-- (on propLists: returns the KEY of the value)
getPos(l, v) -- position of value, 0 if absent
findPos(l, #k) -- propList: position of key, VOID if absent
findPosNear(sortedL, v) -- nearest position in a SORTED list
getLast(l) -- last element
max(l), min(l) -- extremes (also max(a,b,c) form)Sorting
sort(l)- Linear lists sort by value (numbers numerically, strings alphabetically, mixed types by internal type order).
- Property lists sort by key.
- A sorted list stays sorted: subsequent
add()inserts in order rather than appending. This is a real behavioral flag on the list object, not a one-time operation, and code depends on it.
Reference semantics and copying
a = [1, 2, 3]
b = a -- b is the SAME list
b[1] = 99
put a -- [99, 2, 3]
c = a.duplicate() -- independent deep copy- Assignment and parameter passing share the list. Mutations inside a handler are visible to the caller.
duplicate()performs a deep copy (nested lists are copied too).- Equality (
=) on lists compares contents, not identity. - Strings, integers, floats, symbols are value types; lists, property lists, images, and objects are reference types. Points and rects behave as value-ish types in most operations but are implemented as small lists;
duplicate()also works on them.
Lists as data structures
Idioms seen constantly in shipped games:
-- struct via propList
user = [#id: 7, #name: "guest", #pos: point(3, 5)]
-- table of structs
users = []
add users, user
-- lookup table / dictionary
byId = [:]
setaProp(byId, user.id, user) -- integer keys work as prop keys
found = getaProp(byId, 7)
-- queue
add pending, msg -- enqueue
msg = pending[1]
deleteAt(pending, 1) -- dequeue
-- nested score-like data
grid = []
repeat with y = 1 to 10
row = []
repeat with x = 1 to 10
add row, 0
end repeat
add grid, row
end repeat
grid[3][4] = 1Performance notes (for emulator authors)
- Native lists are contiguous arrays;
addAt/deleteAtare O(n) shifts,addon an unsorted list is amortized append,addon a sorted list is a binary-search insert. getaProp/findPoson property lists are linear scans in the general case; sorted property lists allow binary search by key.- Property list keys may be any value type (symbols dominate, but integers and strings occur); key comparison follows Lingo equality, so string keys match case-insensitively and
props["Name"]finds#name.