21. Lists

Writing with Inform

WI §21.1 Lists and entries

Many sections in this book begin by introducing a new kind of value. Reading through in order, the possibilities mount up: numbers, times, texts, and so on. (See the Kinds page of the Index for a convenient list of the options.) This section is a little different: rather than showing a single new kind of value, it shows how to make a new kind out of any existing one.

If K is any kind of value, then "list of K" is also a kind of value. For instance, we could write:

let L be a list of numbers;

and this would create a new "let" variable, called L, whose kind of value is "list of numbers". On the other hand, we are not allowed to write:

let L be a list;

because "list" by itself is not a kind of value. (Inform always needs to know what kinds the values entered in a list are going to have.)

Lists are like flexible-length table columns, but that probably makes them sound more mysterious than they really are. A list is simply a sequence of values, called its "entries", numbered from 1 upwards. The number of entries is called its "length". If we try

let L be a list of numbers;
say "L has [the number of entries in L] entries.";

then we find

L has 0 entries.

This is because all lists start out empty when created: that is, they initially have 0 entries. Inform has two built-in adjectives "empty" and "non-empty" which can apply to lists, and they mean just what they ought to mean: a list is empty if its length is 0, and otherwise non-empty.

We can add entries very easily:

add 2 to L; add 3 to L; add 5 to L;

We can now, for instance, try saying the list:

say "L is now [L].";

with the result

L is now 2, 3 and 5.

Note that only numbers can be added to L: if we try

add "clock" to L;

Inform will produce a problem message, because L has kind "list of numbers", whereas "clock" is text. In this way, Inform ensures that a list always contains values of the same kind throughout. So it's not possible to construct a list whose entries are:

2, "fish", 4 and the Entire Game

Such a list would be very hazardous to deal with, in any case. If what we need is a combination of different kinds of values, tables are a better option.

Finally, note that since "list of numbers" is a kind of value in its own right, so is "list of lists of numbers", and so on - though such lists are trickier to deal with, they are sometimes handy.

WI §21.2 Constant lists

It is convenient to have a concise way to write down a constant list. Just as we could write "231", say, or "7:01 AM" to refer to particular number and time constants, so we can write list constants:

let L be {1, 2, 3, 4};

Inform recognises that "{1, 2, 3, 4}" is a list because of the braces, and looks at the entries inside, sees that they are numbers, and deduces that it is a constant whose kind of value is "list of numbers". L is then a temporary list variable and we can add to it, remove things, and so on as we please - {1, 2, 3, 4} is merely its initial value.

When constructing lists, it is worth noting that Inform requires spaces after the commas (which seems a little harsh, but is necessary because otherwise many sensible literal specifications for units would be impossible - anyway, the reason isn't important here). So

let L be {1,2,3,4};

would produce problem messages. But Inform does not require spaces round its braces.

We call this way of writing a list "brace notation". In mathematics, braces are usually used for sets, and properly speaking these are sequences not sets - so that "{1, 2, 3, 4}" is different from "{4, 3, 2, 1}" - but it is still a familiar notation. Similarly,

let L be {"apple", "pear", "loganberry"};

makes L a list of texts; and

The marshmallow, the firework and the stink bomb are in the Scout Hut. The list of prohibited items is a list of objects that varies. The list of prohibited items is {the firework, the stink bomb}.

makes a global variable ("list of prohibited items") with kind of value "list of objects", and whose initial value is to contain two things: the firework and the stink bomb. More exotically, if we need to make lists of lists:

let L be {{1, 2}, {6, 7, 8}};

gives L the kind of value "list of lists of numbers", with (initially) two entries: the list {1, 2} (a list of numbers), then the list {6, 7, 8} (ditto).

Constant lists are convenient, too, when a column in a table needs to contain lists:

The duck, the orange, the cider, the cinnamon and the orange are in the Kitchen.
Table of Requirements
recipeingredients
"duck à l'orange"{the duck, the orange}
"spiced cider"{the cider, the cinnamon, the orange}

A special word about the constant list "{ }". This means the list with no entries - the empty list. If we try to create a new "let" variable M with

let M be { };

then Inform will produce a problem message, because it cannot tell what sort of list M will be: a list of numbers, or texts, or times, or…? On the other hand, writing

now M is { };

is fine if M already exists, and then does the obvious thing - empties M. Similarly, a table column in which every entry is "{ }" produces a problem message unless the heading for that column spells out the kind of value stored within it: for instance, "ingredients (list of texts)".

All of this is a notation for constant lists only, not some sort of gluing-things-together operation. So this, for instance:

let L be {100, the turn count};

is not allowed, even though "the turn count" is a number: because it is a number that varies, the braces do not contain constants, and therefore this is not a list constant.

WI §21.3 Saying lists of values

Any list L can be said, provided that its contents can be said. For example:

let L1 be {2, 3, 5, 7, 11};
say L1;

produces the text "2, 3, 5, 7 and 11" - unless we have "Use serial comma." set, in which case a comma appears after the 7. We also have the option of using the more formal notation:

say "[(list of values) in brace notation]"

This text substitution produces the list in the form of "{", then a comma-separated list, and then "}", which looks less like an English sentence but more mathematical. Example:

"[list of people in brace notation]"

might produce "{ yourself, Mr Darcy, Flashman }".

If we say a list of lists, then the individual entry lists are always printed in brace notation: the ordinary sentence way would be incomprehensible.

Of course, the values in L1 are written out in number form because L1 is a list of numbers: we could alternatively try

let L2 be {the piano, the music stand};
say L2;

which produces "piano and music stand". Lists of objects can be said in two additional ways:

say "[(list of objects) with definite articles]"

This text substitution writes out the list in sentence form, adding the appropriate definite articles. Example:

let L be {the piano, the music stand};
say "[L with definite articles]";

says "the piano and the music stand".

say "[(list of objects) with indefinite articles]"

This text substitution writes out the list in sentence form, adding the appropriate indefinite articles. Example:

let L be {the piano, the music stand};
say "[L with definite articles]";

says "a piano and a music stand".

Example

Replacing Inform's default printing of properties such as "(closed)", "(open and providing light)", etc., with our own, more flexible variation.

WI §21.4 Testing and iterating over lists

If L is a list, we can interrogate it to see whether it does or does not contain (at least one instance of) any compatible value V:

if (value) is listed in (list of values):

This condition is true if the given value, which must be of a compatible kind, is one of those in the list. For instance, if L is our list of the numbers 2, 3, 5, 7 and 11 then 5 is listed in it but 6 is not.

if (value) is not listed in (list of values):

This condition is true if the given value, which must be of a compatible kind, is not one of those in the list.

We can also repeat running through a list (just as we can with table rows). Thus:

repeat with (a name not so far used) running through (list of values):

This phrase causes the block of phrases following it to be repeated once for each item in the given list, storing that value in the named variable. (The variable exists only temporarily, within the repetition.) Example:

let L be {2, 3, 5, 7, 11, 13, 17, 19};
repeat with prime running through L:
   ...

If the list is empty, nothing happens: the "…" phrase(s) are never tried.

In the next sections, we shall see that it is possible to change, reorder and resize lists. But it's important never to change a list that's being repeated through. The following:

let L1 be {1, 2, 3, 4};
repeat with n running through L1:
   remove n from L1;

leaves L1 containing {2, 4}, since the removals from the list cause it to shuffle back even while we repeat through it - a bad, bad idea.

WI §21.5 Building lists

We have already seen "add… to…". This in fact comes in two forms:

add (value) to (list of values)

This phrase adds the given value to the end of the list. Example:

let L be {60, 168};
add 360 to L;

results in L being {60, 168, 360}. Note that the value is added even if it already occurs somewhere in L; this can be avoided with "if absent". So:

add 168 to L, if absent;

would do nothing - it is already there.

add (list of values) to (list of values)

This phrase adds the first list to the end of the second. Example:

let L be {2, 3, 5, 7};
add {11, 13, 17, 19} to L;

results in L being {2, 3, 5, 7, 11, 13, 17, 19}.

If we don't want to add new entries at the end, we can instead say where they should go:

add (value) at entry (number) in/from (list of values)

This phrase adds the given value so that it becomes the entry with that index number in the list. Example:

let L be {1, 2, 3, 4, 8, 24};
add 12 at entry 6 in L;

sets L to {1, 2, 3, 4, 8, 12, 24}. If there are N entries in L, then we can add at any of entries 1 up to N+1: adding at entry N+1 means adding at the end. The phrase option "if absent" makes the phrase do nothing if the value already exists anywhere in L.

add (list of values) at entry (number) in/from (list of values)

This phrase adds the first list to the second so that it begins at the given position. Example:

let L be {1, 2, 3, 4};
add {4, 8, 12} at entry 3 in L;

results in L being {1, 2, 4, 8, 12, 3, 4}.

A list is allowed to contain duplicates, and the order matters. For instance:

let L be {2, 2, 3};

makes L into "2, 2 and 3". This is a different list to the one made by:

let M be {2, 3, 2};

even though L and M have the same values, repeated the same number of times - for two lists to be equal, they must have the same kind of entry, the same number of entries, and the same entries in each position.

We can also strike out values:

remove (value) in/from (list of values)

This phrase removes every instance of the given value from the list. Example:

let L be {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
remove 1 from L;

results in L being {3, 4, 5, 9, 2, 6, 5, 3}. Ordinarily "remove 7 from L" would produce a run-time problem, since L does not contain the value 7, but using the "if present" option lets us off this: the phrase then does nothing if L does not contain the value to be removed.

remove (list of values) in/from (list of values)

This phrase removes every instance of any value in the first list from the second. Example:

let L be {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
remove {0, 2, 4, 6, 8} from L;

results in L being {3, 1, 1, 5, 9, 5, 3}. If both lists are large, this can be a slow process, and we might do better by sorting them and trying a more sophisticated method. But this is convenient for anything reasonable-sized.

Again, we can also remove from specific positions:

remove entry (number) in/from (list of values)

This phrase removes the entry at the given position, counting from 1 as the first entry. (Once it is removed, the other entries shuffle down.) Example:

let L be {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
remove entry 3 from L;

results in L being {3, 1, 1, 5, 9, 2, 6, 5, 3}.

remove entries (number) to (number) in/from (list of values)

This phrase removes the entries at the given range of positions, counting from 1 as the first entry. (Once they are removed, the other entries shuffle down.) Example:

let L be {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
remove entries 3 to 6 from L;

results in L being {3, 1, 2, 6, 5, 3}.

Example

425.
Robo 1
A robot which watches and records the player's actions, then tries to repeat them back in the same order when he is switched into play-back mode.

WI §21.6 Lists of objects

Lists can be made of values of any kind (including other lists), but lists of objects are especially useful. We could always make these "by hand":

let L be {the pot plant, the foxglove};

But it is usually easier and clearer to use descriptions.

list of (description of values)value

This phrase produces the list of all values matching the given description. Inform will issue a problem message if the result would be an infinite list, or one which is impractical to test: for instance "list of even numbers" is not feasible.

While that works nicely for many kinds of value ("list of recurring scenes", say), it's particularly useful for objects:

let L be the list of open containers;
add the list of open doors to L;

means that L now contains the open containers (if any) followed by the open doors (if any). Or, for example:

let L be the list of things;
remove the list of backdrops from L;

makes a list of all non-backdrops.

As mentioned above, lists of objects can be said in two additional ways:

"[L with definite articles]"
"[L with indefinite articles]"

And as mentioned below, they can be sorted in property value order:

sort L in P order;
sort L in reverse P order;

where P is any value property. In all other respects, lists of objects are no different to other lists.

Examples

Building a fishing pole from several component parts that the player might put together in any order.
427.
Formicidae ★★
Manipulating the order in which items are handled after TAKE ALL.

WI §21.7 Lists of values matching a description

The useful "list of …" syntax can also be used to produce lists of the values matching a description, too. Thus:

let L be the list of non-recurring scenes;
let C be the list of colours;

There is little to say here except for the usual warning that some kinds of value have a range which is too large to make this possible. For instance, Inform could not sensibly represent:

let N be the list of even numbers;

It would just be too large to hold. In general, if we can repeat through, or find the number of, values matching a description, then we can also use "list of" to bring them all together. See the chart of kinds of value in the Kinds index for a project for which kinds of value allow this.

WI §21.8 Sorting, reversing and rotating lists

Any list L can be reversed:

reverse (list of values)

This phrase puts the list in reverse order. The old entry 1 becomes the new last entry, and so on: reversing an empty list or a list containing only one entry leaves it unchanged. Example:

let L be {11, 12, 14, 15, 16, 17};
reverse L;

results in L being {17, 16, 15, 14, 12, 11}.

And any list can similarly be sorted:

sort (list of values)

This phrase puts the list into ascending order. Example:

let L be {6 PM, 11:13 AM, 4:21 PM, 9:01 AM};
sort L;

results in L being {9:01 AM, 11:13 AM, 4:21 PM, 6 PM}.

sort (list of values) in reverse order

This phrase puts the list into descending order. Example:

let L be {6 PM, 11:13 AM, 4:21 PM, 9:01 AM};
sort L in reverse order;

results in L being {6 PM, 4:21 PM, 11:13 AM, 9:01 AM}.

sort (list of values) in random order

This phrase puts the list into a uniformly random order, shuffling it as if it were a pack of cards. Example:

let L be {1, 2, 3, 4, 5, 6};
sort L in random order;

might result in L being {3, 1, 5, 6, 4, 2}. Or any of 719 other arrangements, including being left as it was.

Lists of objects can also be sorted in property value order. For instance,

sort (list of objects) in (property) order

This phrase puts the list into ascending order of the values of the given property for the items in the list; this is only allowed if all of those values do have the property in question. Example:

let L be the list of people;
sort L in carrying capacity order;

would arrange people with weaklings first, titans last.

sort (list of objects) in reverse (property) order

This phrase puts the list into descending order of the values of the given property for the items in the list; this is only allowed if all of those values do have the property in question. Example:

let L be the list of people;
sort L in reverse carrying capacity order;

would arrange people with titans first, weaklings last.

Rotating a list means moving all of its entries along by one place, and then moving the one on the end back to the start. For instance, if L is {1, 2, 3, 4}, then

rotate (list of values)

This phrase shuffles the entries of the list forwards (to the right) by one place, so that the 1st becomes 2nd, the 2nd becomes 3rd, and so on until the last, which becomes the new first entry. Example:

let L be { "cow", "heifer", "bullock" };
rotate L;

results in L being { "bullock", "cow", "heifer" }.

rotate (list of values) backwards

This phrase shuffles the entries of the list backwards (to the left) by one place, so that the 3rd becomes 2nd, the 2nd becomes 1st, and so on; the previous 1st entry becomes the new last entry. Example:

let L be { "cow", "heifer", "bullock" };
rotate L backwards;

results in L being { "heifer", "bullock", "cow"}. (This achieves the same effect as "reverse L; rotate L; reverse L;" but is a little faster, and a lot less effort to read.)

WI §21.9 Accessing entries in a list

The length of a list can change as values are added or removed, and can in principle be any number from 0 upwards. A list with 0 entries is empty. We can find the length with:

number of entries in/of/from (list of values)number

This phrase produces the number of positions in the list. Example:

the number of entries in {1, 1, 1, 3, 1}

is 5, even though there are only two genuinely different items in the list.

If the length is N then the entries are numbered from 1 (the front) to N (the back). These entries can be accessed directly by their numbers. For instance,

entry 2 of L

refers to the second entry of L: it can be used as a value, or changed, just as if it were a named variable. For instance, we could write:

now entry 7 of L is "Spain";
say "The rain in [entry 7 of L] stays mainly in the plain.";

which would (untruthfully) print "The rain in Spain stays mainly in the plain", but only if L had an entry 7 to make use of: if L were a list of 5 entries, say, then a run-time problem results. (And if L cannot hold text, a problem message means that we never get as far as run-time.) Because entries number from 1, this is always incorrect:

entry 0 of L

and if L is currently empty, then there is no entry which can be accessed, so that any use of "entry … of L" would produce a run-time problem. There are programming languages in the world where accessing entry 100 in a 7-entry list automatically extends it to be 100 entries long: Inform is not one of them. But see the next section for how to change list lengths explicitly.

Example

428.
Robo 2 ★★★
A robot which watches and records the player's actions, then tries to repeat them back in the same order when he is switched into play-back mode.

WI §21.10 Lengthening or shortening a list

We can explicitly change the length of a list like so:

change (list of values) to have (number) entries/entry

This phrase alters the given list so that it now has exactly the number of entries given. Example:

change L to have 21 entries;

If L previously had more than 21 entries, they are thrown away (and lost forever); if L previously had fewer, then new entries are created, using the default value for whatever kind of value L holds. So extending a list of numbers will pad it out with 0s, but extending a list of texts will pad it out with the empty text "", and so on.

We can also write the equivalent phrases:

truncate (list of values) to (number) entries/entry

This phrase alters the given list so that it now has no more than the number of entries given. Example:

truncate L to 8 entries;

shortens L to length 8 if it is currently longer than that, trimming entries from the end, but would (for instance) leave a list of length 3 unchanged. Note that

truncate L to 0 entries;

empties it to { }, the list with nothing in.

truncate (list of values) to the first (number) entries/entry

This phrase alters the given list so that it now consists only of the initial part of the list with the given length. Example:

truncate L to the first 4 entries;

turns {1, 3, 5, 7, 9, 11} to {1, 3, 5, 7}.

truncate (list of values) to the last (number) entries/entry

This phrase alters the given list so that it now consists only of the final part of the list with the given length. Example:

truncate L to the last 4 entries;

turns {1, 3, 5, 7, 9, 11} to {5, 7, 9, 11}.

But we don't have to truncate: we can also -

extend (list of values) to (number) entries/entry

This phrase pads out the list with default values as needed so that it now has at least the given length. (If the list is already at least that length, nothing is done.) Example:

extend L to 80 entries;

lengthens L to length 80 if it is currently shorter than that.

For example,

To check sorting (N - a number):
   let L be a list of numbers;
   extend L to N entries;
   repeat with X running from 1 to N:
      now entry X of L is X;
   say "L unrandomised is [L].";
   sort L in random order;
   say "L randomised is [L].";
   sort L;
   say "L in ascending order is [L]."

builds a list of N numbers (initially all 0), fills it with the numbers 1, 2, 3, …, N, then randomly reorders them, then sorts them back again, recovering the original order. The text produced by "check sorting 10" depends partly on chance but might for instance be:

L unrandomised is 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10.
L randomised is 6, 2, 9, 3, 10, 1, 7, 4, 8 and 5.
L in ascending order is 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10.

As with text in the previous chapter, a project which needs really long lists should use the Glulx virtual machine - "check sorting 10000", for instance, would break the default memory environment on the Z-machine, which is very tight, but works fine (if not very rapidly) on Glulx.

Examples

429.
A maze that the player can escape if he performs an exact sequence of actions.
Creating a variant GIVE action that lets the player give multiple objects simultaneously with commands like GIVE ALL TO ATTENDANT or GIVE THREE DOLLARS TO ATTENDANT or GIVE PIE AND HAT TO ATTENDANT. The attendant accepts the gifts only if their total combined value matches some minimum amount.

WI §21.11 Variations: arrays, logs, queues, stacks, sets, sieves and rings

Lists are highly adaptable, and many other collection-like constructions can be made using them. This section introduces no new material, but simply suggests some of the variations which are possible.

1. The traditional computing term array means a list of values accessed by their entry numbers, often used in mathematical computations. The difference between an array and a list is mostly one of attitude, but usually arrays are fixed in length whereas lists can expand or contract.

2. A log is a list which records the most recently arrived values, but does not allow itself to grow indefinitely. In the following, which remembers the seven most recently taken items, new values arrive at the end while old ones eventually disappear from the front:

The most-recently-taken list is a list of objects that varies.
Carry out taking something (called the item):
   truncate the most-recently-taken list to the last 6 entries;
   add the item to the most-recently-taken list.
After taking:
   say "Taken. (So, your recent acquisitions: [most-recently-taken list].)"

Note that the most-recently-taken list begins play as the empty list, grows as the first few items are taken, but then stabilises at length 7 thereafter. If we need to remember recent history, but only recent history, then a log is better than a list which can grow indefinitely, because there is no risk of speed reduction or memory exhaustion in a very long story.

3. A queue is a list of values which are waiting for attention. New values join at the back, while those being dealt with are removed from the front (whereupon the whole queue moves up one). An empty queue means that nobody is waiting for attention: but there is, in principle, no upper limit to the size of a queue, as anyone who has tried to make a couchette reservation at Roma Termini will know.

Queues typically form when two independent processes are at work, but going at different or variable speeds. An empty queue looks just like any other list:

The queue is a list of objects that varies.

(Invariably people, in what follows, but we'll make it a "list of objects" to allow for other possibilities too.) Once we identify a "new customer", we can join him to the queue thus:

add the new customer to the queue;

The process of serving the customers needs to make sure there is actually somebody waiting in the queue before it does anything:

Every turn when the number of entries in the queue is not 0:
   let the next customer be entry 1 of the queue;
   say "[The next customer] is served and leaves.";
   remove entry 1 from the queue.

Of course queues can also be constructed which empty from other positions, rather than the front: or we could make what computer scientists sometimes call a deque, a "double-ended queue" where new values arrive at both ends.

4. A stack is like a queue except that values arrive at, and are removed from, the same end. Stacks are slightly faster if the active end is the back rather than the front, though this will only be noticeable if they grow quite large.

To put a value V onto a stack S (which is known as "pushing") is simple:

add V to S;

And to remove a value from the top of the stack (which is known as "pulling"):

let N be the number of entries in S;
let V be entry N of S;
remove entry N from S;

Note that the middle line, accessing entry N, will fail if N = 0, that is, if the stack is empty: Inform's list routines will produce a run-time problem message.

Stacks are useful if some long-term process is constantly being interrupted by newer and more urgent demands, but they can also be used in planning. If a character has a long-term goal, which needs various short-term goals to be achieved along the way, then a stack can represent the goals currently being pursued. The top of the stack represents what the character is trying to achieve now. If the character realises that it needs to achieve something else first, we put that new goal onto the top of the stack, and it becomes the new current goal. When the character completes a task, it can be removed, and we can go back to trying to finish whatever is now on top. When the stack is empty, the character has achieved the original goal.

5. Notoriously, set has 464 distinct meanings in the Oxford English Dictionary, making it the single most ambiguous word in the language. Here we mean not the home of a badger or the Egyptian god of the desert, but the mathematical sense: a collection of values (sometimes called "elements") without duplicates, and which is normally written in brace notation and in some natural order for the reader's convenience.

The trick here is to maintain the principle that, at all times, our list is sorted in order and contains no duplicates. To provide an example, we start with two sets of numbers:

let S be {2, 4, 8, 16, 32, 64};
let T be {2, 4, 6, 10};

Here we add an element to T:

add 8 to T, if absent; sort T;

The "if absent" clause ensures that no duplicate can occur, and by sorting T afterwards, we maintain the principle that a set must remain in order - so T is now {2, 4, 6, 8, 10}, not {2, 4, 6, 10, 8}. (Inform's sorting algorithm is fast on nearly-sorted lists, so frequent sorting is not as inefficient as it might look.)

We next take the union of T and S, that is, the set containing everything which is in either or both:

let U be S; add T to U, if absent; sort U;

This makes U = {2, 4, 6, 8, 10, 16, 32, 64}, and once again no duplicates occur and we preserve the sorting. The intersection of T and S, the set of elements in both of them, is a little trickier:

let I be T;
repeat with the element running through T:
   if the element is not listed in S, remove the element from I.

(Faster methods could be devised which exploit the sortedness of T and S, but are not worth it for shortish lists.) This produces I = {2, 4, 8}. Lastly, we can form the set difference, consisting of those elements which are in S but not in T:

let D be S; remove T from D, if present;

Here, as with intersection, since all we do is to strike out unwanted elements, the surviving ones remain in order and there is no need to sort when we are finished. This produces D = {16, 32, 64}.

6. A sieve is used to make a complicated choice where there are many constraints, by ruling out impossible cases to see what is left. The term derives from the kitchen utensil (for sieving fine grains of flour), but via the name of the "sieve of Eratosthenes", an ancient Greek method for determining the prime numbers.

Using a sieve is much like using a set, and the difference is mainly one of outlook - we are interested in what does not belong, rather than what does.

7. A ring is not so much a row of values, more a circle, with the last and first entries thought of as adjacent. One position is usually thought of as special, and is the place where new items are added: this may as well be entry 1. For instance, to add "new item" to the ring:

add the item at entry 1 in the ring;

To set "item" to the frontmost value and extract it from the ring:

let the item be entry 1 of the ring;
remove entry 1 from the ring;

And we can rotate the ring in either direction, making a different entry the new entry 1 and therefore the new frontmost value:

rotate the ring;
rotate the ring backwards;

A last note to conclude the chapter on lists. Lists, like almost all other values in Inform, can be passed to phrases as parameters. However, note that they are genuine values, not what some programming languages call "references" or "pointers". So the following:

To mess with (L - a list of numbers):
   add 7 to L, if absent.

does nothing, in practice. If given a list, it adds 7 to the list, but then throws it away again, so the longer list is never seen; it's exactly like

To mess with (N - a number):
   now N is 3.

which can never affect anything other than its own temporary value "N", which expires almost immediately in any case.

If we want a phrase which changes a list in a useful way and gives it back to us, we need a phrase which both takes in and gives back:

To decide which list of numbers is the extended (L - a list of numbers):
   add 7 to L, if absent;
   decide on L.

And then, for example -

the extended { 2, 4, 6 };

produces:

{ 2, 4, 6, 7 }

Examples

Retrieving items from an airport luggage carousel is such fun, how can we resist simulating it, using a list as a ring buffer?
A safe with a multi-number combination, meant to be dialed over multiple turns, is implemented using a log of the last three numbers dialed. The log can then be compared to the safe's correct combination.
The modest Leonardo Fibonacci of Pisa will be only too happy to construct his sequence on request, using an array.
In this fiendishly difficult puzzle, which may perhaps owe some inspiration to a certain BBC Radio panel game (1967-), a list is used as a set of actions to help enforce the rule that the player must keep going for ten turns without hesitation, repetition, or deviating from the subject on the card.
In this evocation of supermarket deli counter life, a list is used as a queue to keep track of who is waiting to be served.
The haughty Eratosthenes of Cyrene will nevertheless consent to sieve prime numbers on request.
Your hard-working mother uses a list as a stack: urgent tasks are added to the end of the list, interrupting longer-term plans.