Suppose you want nine buttons in a grid, or a row of twenty boxes. You could write out every one, but that's a lot of repetition, and they'd each need a different name. An array tag creates any number of tag instances from a single definition, and lets you get at each one by its number.
<i:array size=5 *=<box {This is box number <i>}>>
This is box number 1This is box number 2This is box number 3This is box number 4This is box number 5
An array needs three things:
Each element is created by carrying out the definition. While that happens, the array's name stands for the number of the element being created, which is why each box above shows a different number.
An array always has one current element. The array's primary field holds its number, which starts at 1. To work with an element, make it the current one, then use .* to reach it:
<i:array size=5 *=<box backgroundColor=null {Box <i>}>>
<i 3> <! make element 3 the current element !>
<i.*.backgroundColor {red}> <! change its background !>
<putLn i.*> <! send its contents: "Box 3" !>
i.* works just like the name of the element. You can read it, change it, or reach inside it, exactly as you would with any named tag instance.
A few more things you can do with an array:
| Code | Does |
|---|---|
i.# | generates the number of elements |
<i.@ 2> | generates the primary field value of element 2, without changing the current element |
<i.size 8> | resizes the array: new elements are created at the end, or extra ones deleted from the end |
<i.insert> | creates a new element at the position of the current element, moving the rest along |
<i.*.delete> | deletes the current element; the ones after it move up to fill the gap |
Here's a row of buttons, each meant to send its own number to the program. This version has a bug. Can you spot it?
<b:array size=3 *=<button {Button <b>} onClick=<putLn b>>>
The buttons are labelled 1, 2 and 3 correctly, because the label is worked out while each button is created.
But onClick is a code field, so <putLn b> is only carried out when a button
is clicked, and by then, b means the array's current element number. Every button sends
1.
The fix is for each element to remember its own number while it's being created, in a scalar inside it:
<b:array size=3 *=<button onClick=<putLn me> {<me:scalar b>Button <b>}>>
Each button now has its own me, set to its own number when it was created. Because the onClick code is inside the button, it finds that button's me (the nearest one, by the scope rules from the chapter on instance names).
Remember that replacing a tag's primary field removes any tag instances inside it, including a scalar like me. If you'll be changing what's displayed in an element, put the changing part in a named span of its own, and change that, as in the next example.
This program draws a 3×3 board as an array of nine buttons. The console's only job is to tell the program which square was clicked. The program keeps track of the game, and puts each mark on the board by making the clicked square the current element, then changing the mark span inside it.
board = [''] * 10 # board[1] to board[9]; board[0] is unused
player = 'X'
lines = [(1,2,3), (4,5,6), (7,8,9), (1,4,7), (2,5,8), (3,6,9), (1,5,9), (3,5,7)]
print('''
<console.title {Noughts and Crosses}>
<box width=11,em border=0 {<cells:array size=9 *=<button width=3,em height=3,em onClick=<putLn me>
{<me:scalar cells><mark:span {</ >}>}>>
}></n>
<status:span {X to play}></n>
<button {Quit} onClick=<putLn {/quit}>>
''')
while True:
command = input()
if command == '/quit':
break
if not command.isdigit():
continue
square = int(command)
if board[square]:
continue # already taken
board[square] = player
print('<cells ' + str(square) + '>') # make that square the current element...
print('<cells.*.mark {' + player + '}>') # ...and show the player's mark on it
if any(board[a] == board[b] == board[c] == player for a, b, c in lines):
print('<status {' + player + ' wins!}>')
board = ['done'] * 10 # stop further moves
elif all(board[1:]):
print('<status {A draw.}>')
else:
player = 'O' if player == 'X' else 'X'
print('<status {' + player + ' to play}>')
| X | X | X |
| O | O | |
A few things to notice:
<mul i, 25> gives a
different number for each element. What would make a good colour from it?)