
This post is the second in a series on digital literature. I’m dissecting the JavaScript code of “Gorge”, an infinite poem by J.R. Carpenter that riffs on Nick Montfort’s program “Taroko Gorge”. The first post, which defines “variables”, “strings” and “arrays”, is here.
At the end of the last post, I said that we would continue by looking at how to choose a substring from an array, and what happens from there. In order to understand how the program selects a substring, you have to understand something else first: what a function is, and how it works within a program.
It’s hard to meaningfully define a function in non-technical terms because it is so basic a thing that breaking it down further is difficult; at the same time, too vague a definition will be imprecise and therefore meaningless. The best I can do for you right now is to say that functions are basically chunks of code that, as the name might suggest, do something. When you call a function, you’re invoking the code in it. If similes are your thing, you could say that calling a function is like casting a spell: you can have the spell written down, but it doesn’t do anything until you bring it into existence.
Functions have names, arguments, and bodies. This is probably best illustrated by actual code:
function rand_range(max) {
return Math.floor(Math.random() * (max+1));
}
The name of that function is rand_range
, the argument that we pass to it is max
, and the second line (beginning with the word return
) is the body of the function. It is, in other words, what the function will do, when it is called, with the argument max
. max
, as you may have guessed by now, is just a variable representing a maximum value. If we say that max
is 3, the function will execute with 3 taking max
‘s place.
What does that mean, practically speaking? What will the function actually do when called? Its name should give you a clue: looking at something called rand_range
, it’s a safe bet that it’ll have something to do with a random number that falls within a predetermined range. Add the fact that the argument has been named max
, and you can further guess that the value of max
represents the top end of that predetermined range. Now all you have to contend with is Math.floor
and Math.random
. Math.floor
is a function (functions can call each other) that returns the largest integer less than or equal to a given number (in this case, max
); Math.random
returns a pseudo-random (which is to say, more or less random for most purposes but not strictly random, since that’s pretty hard to achieve) integer between 0 (inclusive) and 1 (exclusive). You can scale that up or down; in the event that max
is 3, as I suggested earlier, Math.random
returns an integer between 0 and 3, not including 3. This is why the function includes (max + 1)
, which extends our range by 1, allowing us to include 3.
The next function is called choose
. It exists in order to produce a random selection from the arrays that we discussed earlier, the ones like above
(“appetite, brain, craving, desire”), below
(“aroma, bladder, blood vessel, bowl”), and trans
(“agitate, attract, bite, boil”). choose
looks like this:
function choose(array){
return array[rand_range(array.length-1)];
}
When you insert the name of an array—let’s say above
—[rand_range(array.length-1)]
selects a random value from that array. (The -1 bit is there because the first thing in an array is denoted Thing 0. Computers like to start counting at 0. So -1 just means that you get the whole range of the array from the first (zeroth?) item to the last.) Then return array
returns the index that’s been selected. So if we were to do this with above
, the index might be, e.g., 3, which would return ‘digestive juice’ (since we’re counting from 0, remember!)
The next function is where we start to get into the actual construction of the poem, and it’s followed by two other functions that are very similar. These functions haven’t been renamed since their inception as part of Taroko Gorge, so they may at first glance appear irrelevant to the content of Gorge, but they’re doing the right things. There’s a section in the middle of this first function that doesn’t work in Gorge, which you’ll probably be able to spot straight away. It doesn’t do anything bad; it just doesn’t get evaluated.
function path() {
var p=rand_range(1);
var words=choose(above);
if ((words=='forest')&&(rand_range(3)==1)){
words='monkeys '+choose(trans);
} else {
words+=s[p]+' '+choose(trans)+s[(p+1)%2];
}
words+=' the '+choose(below)+choose(s)+'.';
return words;
}
And breathe.
var p
is defined as rand_range(1)
, which we know means “a random whole number between 0 and 1”. Practically speaking, that means “either 0 or 1”. We don’t know what we’ll be doing with var p
yet, so we’ll just put that away for later use.
var words=choose(above)
, as we know from our adventures with choose(array)
previously, means that words
is set to a random selection from the list of words in the array above
.
The if
statement says that if the value of words
happens to be “forest”, and if the rand_range
value is 1 (with the max
value defined as 3, so the value could be 0, 1, 2 or 3), then “forest” will be replaced with the word “monkeys” and a random selection from the trans
array. Since we don’t have the word “forest” in any of our arrays, this piece of code will actually never be used. It’s a leftover from Montfort’s original version.
else
means “otherwise”, so this is where we can start paying attention again: if the conditions of the if
statement are not met (and they won’t be), this is what will happen next.
words+=s[p]
is a random pluralizer. It takes a var
from earlier up in the program, which I realize now I may have skated over somewhat in the first post. This is s
, which is defined as 's,'.split(',')
. What this means is that s
is actually another array, split on its comma, so that the array consists of the letter ‘s’ and nothing, known in programming as ‘the empty string’. Therefore, words+=s[p]
means that when p=0
, s
will be added on to the end of whatever value we got from words
. (words+=s[p]
is a short-hand for words=words+s[p]
. “Adding” a string means that it is glued on to the end of whatever comes before.) As we start counting from 0, that means a letter ‘s’ will be added, making the word plural. When p
is 1, the second element of the array s
will be added to the end of words
, which in this case means nothing at all.
After this will come a space (signified by ' '
), no matter whether the value of var words
has been pluralized or not. Then a word will be selected from the trans
array, which will be pluralized half of the time. This is signified by (p+1) % 2
, where %
means “the remainder after division by”. Cast your mind back to elemento-primary school! When p
is 0, we get 0+1, which is 1, and the remainder of 1 divided by 2 is 1. We’ll add element 1 of array s
again, which is empty string. When p
is 1, we get 1+1, which is 2, and the remainder of 2 divided by 2 is 0, so we’ll append the first element of array s
(remember, the first element is element 0!), which is the letter ‘s’.
The final section has the addition of the word ‘the’ to whatever comes out of words
, a random selection from the array below
, a random selection from the array s
(another choice between pluralizing or not), and the addition of a full stop. return words
is the piece of code that will actually return the value for function path()
, when it’s called.
More next time, including a couple of other functions that select words from arrays using different criteria, and the beginnings of how the program fits all of these functions together to produce the poem itself.