User Tools

Site Tools


logo:tutorial:chapter10

Chapter 10: Words, Lists and Numbers

Logo data consists of characters that can be combined to make words and lists. Sounds too simple, doesn't it? In one sense, all Logo data is just characters. However, Logo interprets sequences of characters as different types of data, depending on the characters used. In fact, so do you. You interpret the sequence 100 as the number one hundred - a numeric value. To Logo, the sequence 100 is a three-character word. A word that can be interpreted as a numeric value can be used as a number. Usually, a word needs a double quotation mark in front of it. However, Logo allows you to forget about the double quotation mark when you enter a number - it's like a hidden shortcut because Logo knows that people do not think of numbers as words. And, there are certain manipulations that can be done to numbers that are not valid for words. For these reasons, numbers are covered in their own section.

As you work your way through the following sections, you will notice that some of the same commands are used more than once. Many of the commands for words also work with lists. And, since numbers are special words, some of the commands work for numbers, too. Each section is written specifically for each type of Logo data so that the examples and explanations in each section are consistent with the type of data. You'll find that having commands that work equally well with either words or lists is very convenient. Learning how to use the commands for words is best done with words only. It avoids any specific qualifications for using them with lists and helps to keep you focused on how words are affected.

Computing with Words

Most words in Logo are no different than what you think of as words. A space tells you that a word has ended and the same is normally true for Logo. You also use special punctuation characters that show the end of a word, but they are not considered to be part of the word - like the comma, for example, or the period at the end of a sentence. In Logo, a period or comma is just another character.

Sometimes, you put a word in quotes to make it appear “special” - it's still a word but it stands out from the rest. In Logo, there are special characters that are not normally part of a word because they usually have a special meaning. If you want to use a special character as a regular character, you have to use the backslash character to tell Logo to ignore the special meaning. For example, the space character usually separates words, but you can include a space if you want. Type:

PRINT "A WORD WITH SPACES
A WORD WITH SPACES

The one character immediately following the backslash is treated as a regular character. In this case, the spaces are not separating one word from another - the spaces are part of the word that is printed. Without the backslash, the space is treated as a normal word separator. Type:

PRINT "A WORD WITH SPACES
WITH is neither a procedure nor a name

Another way to “escape” the special meaning of special characters is to use the vertical bar characters - one at the beginning and one at the end. For example, type:

PRINT "|A word with spaces|
A word with spaces

You still need the double quotation mark to tell Logo that a word follows. The vertical bars not only allow special characters to be part of the word, but they also prevent Logo from changing the characters to uppercase. Character sequences enclosed in vertical bars are called “strings” of characters, but they are still words to Logo.

What if you need to use a backslash or a vertical bar as a regular character? You have to put a backslash in front of it. That means it takes two backslashes to get one backslash. Type:

PRINT "BACKSLASH
BACKSLASH

PRINT "VERTICAL|BAR
VERTICAL|BAR

The backslash and vertical bar characters are useful, and sometimes necessary. If you want to quote a word, for example, you can use one or the other. Type

PRINT "|"quote"|
"quote"

PRINT ""quote"
"QUOTE"

Don't let these unusual forms of words bother you. Just be aware that there are special times when special characters are used in special ways for special purposes.

Most of the words you've used so far were complete words that you typed in. There are times when it's necessary to use just a part of a word and then there are times when you need to take the parts and put a word together. There are Logo commands to put words together and other commands to take them apart. However, none of these commands actually change anything. You can use the results of these commands in any way that you would use words that you type - print them, use them as names, or assign them to variables. Comparing one word to another can be important for making decisions. Knowing more about words can make your Logo experience more fun.

Taking Words Apart

Just so you can see that taking a word apart does not change it, enter the following command to create the :SAMPLE variable with the value FLOWERS:

MAKE "SAMPLE "FLOWERS

The FIRST command outputs the first element of the word you give it as input. Each character is an element, or member, of the word. Type:

FIRST :SAMPLE
Result: F

The output of FIRST is just the character F - the first element of the word FLOWERS. The value of :SAMPLE was only used as input to the FIRST command. Type:

SHOW :SAMPLE
FLOWERS

None of the commands that take words apart actually change the input word. The output of these commands is made up from copies of the parts of the input. If you want the value of a variable changed, you have to use MAKE. For example, type:

MAKE "SAMPLE FIRST :SAMPLE
SHOW :SAMPLE
F

Now, :SAMPLE has a new value which is the same as the first element of its original value. The rest of the examples will use the literal word as input rather than a variable - it's easier to follow what's going on that way.

Another way to get the first element of a word is with the ITEM command. It needs two inputs: the number of the element you want and the word from which to get it. Type:

ITEM 1 "FLOWERS
Result: F

The result is the same as with FIRST, which is really just an abstraction of ITEM 1. ITEM can be used to get any element of a word, from the first to the last. It can be handy because there are no commands called SECOND, THIRD and so on.

Many Logo commands do the opposite of another command. The LAST command outputs the last element of the word you give it as input. Type:

LAST "FLOWERS
Result: S

The ITEM command returns the last element if you give it the number of elements. Type:

ITEM COUNT "FLOWERS "FLOWERS
Result: S

The result is the same as with LAST, but it's much easier to use LAST.

FIRST and LAST output a single character. Sometimes, it's useful to get the rest of the word they leave behind - either all but the first element or all but the last element. Logo has two commands that do just that - BUTFIRST and BUTLAST which have the shortcuts BF and BL. Type:

BUTFIRST "FLOWERS
Result: LOWERS

BUTLAST "FLOWERS
Result: FLOWER

You can remove all occurrences of one word from another word with BUTMEMBER (BM for short). It takes two words as input and outputs the result of removing the first word from the second word. This may seem to be an odd thing to do, but a word can be just one character. Type:

BM "E "BEEKEEPER
Result: BKPR

If the first word is more than one character, then all occurrences of the sequence of characters are removed - not the individual characters. Type:

BM "KEE "BEEKEEPER
Result: BEEPER

The FROMMEMBER command (FM for short) takes two words as input. It finds the first occurrence of the first word in the second word and outputs the characters from that point to the end of the second word. Type:

FM "E "BEEKEEPER
Result: EEKEEPER

The first word can be more than one character. Type:

FM "KEEP "BEEKEEPER
Result: KEEPER

Sometimes, getting a character at random is useful. You've used the PICK command to select random colors from a list, but it works for words, too. Suppose you want to simulate tossing a coin to see if it comes up heads or tails. You could do that with PICK and the two-character word HT or TH. Either way, one of the letters will be chosen at random. Don't bet on which one will show up. Type:

PICK "HT
Result: H

You could simulate flipping a coin any number of times and tallying the results in variables named H and T. The FLIP procedure takes a number as input and runs the PICK command that many times. PICK will output either the character H or T and pass it along to TALLY. The TALLY procedure uses indirect reference with its input word to add 1 to the value of the variable whose name is the input word. The variables H and T are created as local variables in FLIP. Because of dynamic scope, TALLY has access to them. Define the procedures and then run FLIP to see what results you get.

FLIP 100
50 HEADS
50 TAILS

Putting Words Together

Just so you can see that putting a word together does not change anything, enter the following commands to create two variables:

MAKE "PART1 "FLO
MAKE "PART2 "WERS

The WORD command normally takes two words as input and combines them to form a new word. Type:

WORD :PART1 :PART2
Result: FLOWERS

The output of WORD is a new word made from the two parts. The parts are not affected. Type:

(SHOW :PART1 :PART2)
FLO WERS

None of the commands that put words together actually change the input parts. The output of these commands is made up from copies of the inputs. If you want the value of a variable changed, you have to use MAKE. For example, type:

MAKE "PART1 WORD :PART1 :PART2
SHOW :PART1
FLOWERS

Now, PART1 has a new value. The rest of the examples will use the literal words as inputs rather than variables - it's easier to follow what's going on that way.

A new word can be put together with more than two parts by enclosing the WORD command and its inputs within parentheses. Type:

(WORD "TIC- "TAC- "TOE)
Result: TIC-TAC-TOE

The FPUT command outputs a new word by putting its first input in front of its second input. In other words, the first input is put first in the new word. Type:

FPUT "FLO "WERS
Result: FLOWERS

The opposite of FPUT is the LPUT command. LPUT outputs a new word by putting its first input at the end of its second input. In other words, the first input is put last in the new word.Type:

LPUT "WERS "FLO
Result: FLOWERS

You can change all occurrences of one word in another word with the SUBST command. It replaces all occurrences of its first input with its second input wherever the first input appears in the word you give it as the third input. Type:

SUBST "E "O "BEEKEEPER
Result: BOOKOOPOR

The first inputs to SUBST don't have to be single characters. Type:

SUBST "EEK "OOKK "BEEKEEPER
Result: BOOKKEEPER

The CHAR command outputs a one-character word. It needs the ASCII value of the character as input. Sometimes, you may need to use a character that you can't type in from the keyboard. For example, the Carriage Return character has the ASCII value of 13 and causes the cursor to move to the beginning of the next line. Type:

PRINT SUBST ". CHAR 13 "A.B.C.D.E
A
B
C
D
E

The Carriage Return character is substituted for the periods. When the result is printed, it may look like five one-character words were printed, but it's really one nine-character word.

Creating new words from parts can be fun. The PIG.LATIN procedure creates a new word from four parts. The first part is the BUTFIRST of the input word; the second part is a hyphen; the third part is the FIRST character of the input word; the fourth part is the syllable AY. Define PIG.LATIN and try some examples.

PIG.LATIN "LOGO
Result: OGO-LAY

Use FOREACH to convert a list of words. Type:

FOREACH [BIG DOGS DIG DEEP HOLES] [PRINT PIG.LATIN "?] 
IG-BAY
OGS-DAY
IG-DAY
EEP-DAY
OLES-HAY

The PIG.LATIN procedure works fine with words that have only one consonant at the beginning. There are more rules to Pig Latin that you can add on your own.

Comparing Words

When you compare two things, you are asking a question. The Logo commands for doing comparisons have a question mark at the end of their names. For example, WORD? is a command that asks if its input is a Logo word. The answer (or output) to the question is either the word TRUE or the word FALSE. One way to use WORD? is in the TEST command. Type:

TEST WORD? "LOGO

TEST is a rather special command. It doesn't output anything, but it does “remember” the result of the test in a variable called @COND. If you want to run some commands when the result of the test is true, you can use the IFTRUE command and give it a list of instructions to run. Type:

IFTRUE [PRINT [YES, THAT'S A WORD]]
YES, THAT'S A WORD

IFTRUE checks the value of @COND and if it's TRUE, the instructions in the list are run. The value of @COND is not changed until another TEST is done. You can put as many IFTRUE commands as you want after the TEST command. The opposite of IFTRUE is IFFALSE. Since the current value of @COND is TRUE, an IFFALSE command at this point would not mean anything. Type:

TEST WORD? [FLOWER]
IFTRUE [PRINT "SMELL]
IFFALSE [PRINT [ALL'S WELL]]
ALL'S WELL

A one-word list is still a list - not a word. The IFTRUE command does not run its instructions in this example. IFFALSE checks the value of @COND and if it's FALSE, the instructions in the list are run.

An alternative to TEST is IF. In one form, IF is a combination of TEST, IFTRUE and IFFALSE.

IF WORD? [FLOWER] [PRINT "SMELL] [PRINT [ALL'S WELL]]
ALL'S WELL

The first input to this form of IF is the same as the input to TEST - a condition that returns either the word TRUE or the word FALSE. The IF command does not make use of the @COND variable; it uses the result of the test to determine what to do next. The second input is the list of instructions to run when the condition is true. The third input is optional, but if you use it, the instructions in the list are run when the condition is false. Another form of IF is similar but does not have lists of instructions. Instead, it uses the special words THEN and THEN as markers. When the test condition is true, the instructions after THEN and up to ELSE are run. ELSE is optional, so the instructions for THEN end at the end of the line if ELSE is not used. When the test condition is false, the instructions after ELSE and up to the end of the line are run. It's more like the way people say things when they make a decision. The following IF command is really the same as the example above:

IF WORD? [FLOWER] THEN PRINT "SMELL ELSE PRINT [ALL'S WELL]
ALL'S WELL

The form of comparison you use is a matter of personal choice - one way works just as well as another. For more information, look in the online help and the Reference Manual.

The empty word has nothing in it, but it's still a word. You test for the empty word with the EMPTY? command. It outputs TRUE if the word is empty and FALSE if it's not empty. If you remove all the characters of a word, you'll end up with the empty word. In the following example, use the up-arrow key and insert additional $comd[accessors|BUTFIRST] commands after you try each line. Type:

EMPTY? BUTFIRST "BOX
Result: FALSE
EMPTY? BUTFIRST BUTFIRST "BOX
Result: FALSE
EMPTY? BUTFIRST BUTFIRST BUTFIRST "BOX
Result: TRUE

Testing for the empty word is necessary when you want to use all of the characters in a word, one at a time. You know you're done when you find the empty word. The PRINT.DOWN procedure takes a word as input and prints it, one character at a time. It checks for the empty word in order to know when to stop printing. After the first character is printed, PRINT.DOWN is run with a slightly shorter word - the BUTFIRST of the original input word. Define PRINT.DOWN and then type:

PRINT.DOWN "FLOWERS
F
L
O
W
E
R
S

What do you think would happen if you changed FIRST to LAST and BUTFIRST to BUTLAST?

A common test in linguistics is whether or not a particular letter is a vowel. The normal vowels are the letters A, E, I, O and U. The MEMBER? command takes two inputs and tests to see if the first input is a member of the second input. If a match is found, MEMBER? outputs TRUE, otherwise, MEMBER? outputs FALSE. The first input does not have to be a one-character word, but that's what you'd need to test for vowels. Type:

MEMBER? "E "AEIOU
Result: TRUE

The PIG.LATIN procedure you defined earlier should just add the syllable AY to the end of a word that begins with a vowel. Add the following test at the beginning of PIG.LATIN.

IF MEMBER? FIRST :INPUT "AEIOU THEN OUTPUT WORD :INPUT "-AY

There are more rules to Pig Latin, but this is an improvement-ay? Have you noticed that the usual form of a Logo instruction is the command word followed by its inputs, if it needs any? This form is called prefix notation - the command word comes first. You can do every Logo command this way and it's easy to remember the format. Some command words look more natural if they go in between their inputs. This is called infix notation and the command words are called operators - in fact, most operators are really symbols, like =, <, and >. Logo has both commands and operators for the comparisons: equal, greater than, greater than or equal, less than, less than or equal, and not equal.

All of the comparison operators (represented by symbols) can be used in either a prefix or infix notation. The commands (represented by words) can only be used in a prefix notation. You can often avoid using parentheses to make Logo understand what you are doing if you use prefix notation instead of infix notation. Some examples will show you where parentheses are needed. It's not a big problem, as long as you're aware of it.

To see if two words are equal to each other, you can use either of the commands = or .EQ or the operator =. Each of the following commands work the same:

EQUAL? "WILD "WILD
Result: TRUE

.EQ "WILD "WILD
Result: TRUE

= "WILD "WILD
Result: TRUE

"WILD = "WILD
Result: TRUE

You may need parentheses for the infix notation when the first (or left) input is more complicated than a simple word. It's often a very subtle problem.

EQUAL? FIRST "WILD "W
Result: TRUE

FIRST "WILD = "W
Result: F

Result F? What is that? The = operator has used the word WILD as its first (left) input and compared it to W. Since they were not equal, it output FALSE which was then input to FIRST which output the first character of FALSE which is where the F came from. In cases like this, you need to put parentheses around the first input to an infix operator or change the test to have the simple word as the first input.

(FIRST "WILD) = "W
Result: TRUE

"W = FIRST "WILD
Result: TRUE

You can reverse the meaning of a test with the NOT command. For the prefix notation, just put the NOT command in front of the comparison command word or symbol. If you used the infix notation, you can put NOT in front of the first (left) input. However, you don't really need to use NOT because Logo has all the comparisons you need. It's probably easier and clearer to change the command or operator.

You can read about the rest of the comparison commands and operators in the online help or the Reference Manual. They are an important part of programming. Combined with the TEST and IF commands, they add a sense of intelligence to your procedures.

Computing with Lists

Most of the lists you've used so far were complete lists that you typed in. There are times when it's necessary to use just a part of a list and then there are times when you need to take the parts and put a list together. There are Logo commands to put lists together and other commands to take them apart. However, none of these commands actually change anything. You can use the results of these commands in any way that you would use lists that you type - print them, assign them to variables and, if they contain commands, give them to the RUN command. Comparing one list to another can be important for making decisions. Knowing more about lists can make your Logo experience more fun.

To you, a list of words is a sentence. The same is true for Logo. A sentence in Logo is a special kind of list that contains only words - it can not have other lists as elements.

Many of the lists you have used were actually Logo sentences. But, since a sentence is still a list, it didn't seem worth mentioning. There are times, however, when a sentence is what you want Property lists are another special kind of list. They are discussed in Chapter 6 - Property Lists.

Taking Lists Apart

Just so you can see that taking a list apart does not change it, enter the following command to create the :SAMPLE variable with the value [WILD FLOWERS]:

MAKE "SAMPLE [WILD FLOWERS]

The FIRST command outputs the first element of the list you give it as input. The elements, or members, of a list can be words or other lists. Type:

FIRST :SAMPLE
Result: WILD

The output of FIRST is just the word WILD - the first element of the list [WILD FLOWERS]. The value of :SAMPLE was only used as input to the FIRST command. Type:

SHOW :SAMPLE
[WILD FLOWERS]

None of the commands that take lists apart actually change the input list. The output of these commands is made up from copies of the parts of the input. If you want the value of a variable changed, you have to use MAKE. For example, type:

MAKE "SAMPLE FIRST :SAMPLE
SHOW :SAMPLE
WILD

Now,SAMPLE has a new value which is the same as the first element of its original value - the word WILD. In Logo, a variable can contain any valid Logo data at any time. There is no restriction that the value has to be of the same type of data all the time. Be aware of this - sometimes it can cause problems. The rest of the examples will use the actual inputs rather than a variable - it's easier to follow what's going on that way.

Another way to get the first element of a list is with the ITEM command. It needs two inputs: the number of the element you want and the list from which to get it. Type:

ITEM 1 [WILD FLOWERS]
Result: WILD

The result is the same as with FIRST, which is really just an abstraction of ITEM 1. ITEM can be used to get any element of a list, from the first to the last. It can be handy because there are no commands called SECOND, THIRD and so on.

Many Logo commands do the opposite of another command. The LAST command outputs the last element of the list you give it as input. Type:

LAST [WILD FLOWERS]
Result: FLOWERS

The ITEM command returns the last element if you give it the number of elements. Type:

ITEM COUNT [WILD FLOWERS] [WILD FLOWERS]
Result: FLOWERS

The result is the same as with LAST, but it's much easier to use LAST.

FIRST and LAST output a single element. Sometimes, it's useful to get the rest of the list they leave behind - either all but the first element or all but the last element. Logo has two commands that do just that - BUTFIRST and BUTLAST which have the shortcuts BF and BL. Type:

BUTFIRST [WILD FLOWERS]
Result: [FLOWERS]

BUTLAST [WILD FLOWERS]
Result: [WILD]

You can remove all occurrences of one element from a list with BUTMEMBER (BM for short). It takes two inputs and outputs the result of removing the first input from the list you give as the second input. The first input can be either a word or a list. Type:

BM "WILD [WILD FLOWERS AND WILD HORSES]
Result: [FLOWERS AND HORSES]

A list can have other lists as elements. Type the following example which uses a list of two lists as the second input:

BM "WILD [[WILD FLOWERS] [WILD HORSES]]
Result: [[WILD FLOWERS] [WILD HORSES]]

The word WILD is not matched with either of the WILD words in the second input because the word WILD is not an element of the second input - the second input has two elements, both of which are lists. Type:

BM [WILD HORSES] [[WILD FLOWERS] [WILD HORSES]]
Result: [[WILD FLOWERS]]

The FROMMEMBER command (FM for short) takes two inputs. It finds the first occurrence of the first input in the list you give it as the second input and outputs a list with the elements from that point to the end of the second input. The first input can be either a word or a list.Type:

FM "FLOWERS [WILD FLOWERS HORSES MUSHROOMS]
Result: [FLOWERS HORSES MUSHROOMS]

Sometimes, getting an element of a list at random is useful. Suppose you want to make random noise. You could define a list of PLAY commands and then randomly select one to play. Type:

MAKE "NOISE [[PLAY "CARHORN] [PLAY "WASP] [PLAY "TOILETFLUSH]]

When you want the noise to play, just type:

RUN PICK :NOISE

Putting Lists Together

Just so you can see that putting a list together does not change anything, enter the following commands to create two variables:

MAKE "PART1 [WILD]
MAKE "PART2 [FLOWERS]

The LIST command normally takes two inputs and combines them to form a new list. Type:

LIST :PART1 :PART2
Result: [[WILD] [FLOWERS]]

The output of LIST is a new list made from the two parts. The parts are not affected. Type:

(SHOW :PART1 :PART2)
[WILD] [FLOWERS]

None of the commands that put lists together actually change the input parts. The output of these commands is made up from copies of the inputs. If you want the value of a variable changed, you have to use MAKE. For example, type:

MAKE "PART1 LIST :PART1 :PART2
SHOW :PART1
[[WILD] [FLOWERS]]

Now, PART1 has a new value. The rest of the examples will use the actual inputs rather than variables - it's easier to follow what's going on that way.

The LIST command maintains the structure of its inputs in the list that it creates. In the example above, both of the inputs were lists, so the output was a list of two lists. LIST can use words as inputs, too. Type:

LIST "WILD "FLOWERS
Result: [WILD FLOWERS]

The input words are still words within the output list. This type of list is called a sentence in Logo. The SENTENCE command (SE for short) outputs a list, too. However, it does not maintain the structure of its inputs like LIST does. The output of LIST and SENTENCE are the same sometimes, but not always. Type:

SE "WILD "FLOWERS
Result: [WILD FLOWERS]

With two words as input, the output of SENTENCE is the same as LIST. However, type:

SE [WILD] [FLOWERS]
Result: [WILD FLOWERS]

LIST [WILD] [FLOWERS]
Result: [[WILD] [FLOWERS]]

This is an important and significant difference. If you need a list with just words, then use SENTENCE to create the list and to modify it. If you need to maintain the structure of the elements of a list, then use LIST to create the list and to modify it. Mixing the use of SENTENCE and LIST can lead to problems. If you accidentally give SENTENCE a list with other lists as elements, the result will not be a sentence, as you might have expected. Type:

SE [WILD FLOWERS] [[WILD HORSES] [WILD MUSHROOMS]]
Result: [WILD FLOWERS [WILD HORSES] [WILD MUSHROOMS]]

The output of SENTENCE in this case is not a sentence because there are lists as elements. Be careful.

A new list can be put together with more than two parts by enclosing the command and its inputs within parentheses. Type:

(LIST "BIG "DOGS "DIG "DEEP "HOLES)
Result: [BIG DOGS DIG DEEP HOLES]

(SE "BIG "DOGS "DIG "DEEP "HOLES)
Result: [BIG DOGS DIG DEEP HOLES]

Since all of the inputs were words, the output of both LIST and SENTENCE are the same. You can also create a new one-element list. Type:

(LIST "ONE)
Result: [ONE]

(SE "ONE)
Result: [ONE]

The FPUT command outputs a new list by putting its first input in front of its second input. In other words, the first input is put first in the new list. Furthermore, FPUT maintains the structure of its first input within the new list so that the FIRST of the new list is the same as the first input to FPUT. Type:

FPUT "WILD [FLOWERS]
Result: [WILD FLOWERS]

FIRST FPUT "WILD [FLOWERS]
Result: WILD

If the first input to FPUT is a list, the structure of it is still maintained in the output. Type:

FPUT [WILD FLOWERS] [[WILD HORSES] [WILD MUSHROOMS]]
Result: [[WILD FLOWERS] [WILD HORSES] [WILD MUSHROOMS]]

FIRST FPUT [WILD FLOWERS] [[WILD HORSES] [WILD MUSHROOMS]]
Result: [WILD FLOWERS]

The opposite of FPUT is the LPUT command. LPUT outputs a new list by putting its first input at the end of its second input. In other words, the first input is put last in the new list. Furthermore, FPUT maintains the structure of its first input within the new list so that the LAST of the new list is the same as the first input to LPUT. Type:

LPUT "FLOWERS [WILD]
Result: [WILD FLOWERS]

LAST LPUT "FLOWERS [WILD]
Result: FLOWERS

If the first input to LPUT is a list, the structure of it is still maintained in the output. Type:

LPUT [WILD MUSHROOMS] [[WILD FLOWERS] [WILD HORSES]]
Result: [[WILD FLOWERS] [WILD HORSES] [WILD MUSHROOMS]]

LAST LPUT [WILD MUSHROOMS] [[WILD FLOWERS] [WILD HORSES]]
Result: [WILD MUSHROOMS]

You can change all occurrences of one word in a list with the SUBST command. It replaces all occurrences of its first input with its second input wherever the first input appears in the list you give it as the third input. Type:

SUBST "WILD "TAME [WILD HORSES]
Result: [TAME HORSES]

The substitution is done recursively, which means that it takes place in the lists within a list, too. Type:

SUBST "WILD "TAME [[WILD FLOWERS] [WILD HORSES]]
Result: [[TAME FLOWERS] [TAME HORSES]]

Creating new lists from parts can be fun.

MAKE "MESSAGE [BIG DOGS DIG DEEP HOLES]
MAKE "SECRET []
FOREACH :MESSAGE [MAKE "SECRET LPUT PIG.LATIN "? :SECRET]
PRINT :SECRET
IG-BAY OGS-DAY IG-DAY EEP-DAY OLES-HAY

SECRET begins as the empty list. As FOREACH converts the words in MESSAGE, the new words are added to the end of SECRET with LPUT. Do you think FPUT would work? Try it and see. What about SENTENCE? Another way to make random noise is to have a list of quoted words and then build the instruction to give to the RUN command. Type:

MAKE "NOISE ["CARHORN "WASP "TOILETFLUSH]
RUN LIST "PLAY PICK :NOISE

Comparing Lists

When you compare two things, you are asking a question. The Logo commands for doing comparisons have a question mark at the end of their names. For example, LIST? is a command that asks if its input is a list. The answer (or output) to the question is either the word TRUE or the word FALSE. One way to use LIST? is in the TEST command. Type:

TEST LIST? [TERRAPIN LOGO]

If you want to run some commands when the result of the test is true, you can use the IFTRUE command and give it a list of instructions to run. Type:

IFTRUE [PRINT [YES, THAT'S A LIST]]
YES, THAT'S A LIST

IFTRUE checks the value of @COND and if it's TRUE, the instructions in the list are run. The value of @COND is not changed until another TEST is done. You can put as many IFTRUE commands as you want after the TEST command. The opposite of IFTRUE is IFFALSE. Since the current value of @COND is TRUE, an IFFALSE command at this point would not mean anything. Type:

TEST LIST? "FLOWER
IFTRUE [PRINT "SMELL]
IFFALSE [PRINT [ALL'S WELL]]
ALL'S WELL

The IFTRUE command does not run its instructions in this example. IFFALSE checks the value of @COND and if it's FALSE, the instructions in the list are run.

An alternative to TEST is IF. In one form, IF is a combination of TEST, IFTRUE and IFFALSE.

IF LIST? "FLOWER [PRINT "SMELL] [PRINT [ALL'S WELL]]
ALL'S WELL

The first input to this form of IF is the same as the input to TEST - a condition that returns either the word TRUE or the word FALSE. The IF command does not make use of the @COND variable; it uses the result of the test to determine what to do next. The second input is the list of instructions to run when the condition is true. The third input is optional, but if you use it, the instructions in the list are run when the condition is false. Another form of IF is similar but does not have lists of instructions. Instead, it uses the special words THEN and

ELSE as markers. When the test condition is true, the 

instructions after THEN and up to ELSE are run. ELSE is optional, so the instructions for THEN end at the end of the line if ELSE is not used. When the test condition is false, the instructions after ELSE and up to the end of the line are run. It's more like the way people say things when they make a decision. The following IF command is really the same as the example above:

IF LIST? "FLOWER THEN PRINT "SMELL ELSE PRINT [ALL'S WELL]
ALL'S WELL

The form of comparison you use is a matter of personal choice - one way works just as well as another. For more information, look in the online help and the Reference Manual.

The empty list has nothing in it, but it's still a list. You test for the empty list with the EMPTY? command. It outputs TRUE if the list is empty and FALSE if it's not empty. If you remove all the elements of a list, you'll end up with the empty list. In the following example, use the up-arrow key and insert additional BUTFIRST commands after you try each line. Type:

EMPTY? BUTFIRST [FLOWERS HORSES MUSHROOMS]
Result: FALSE

EMPTY? BUTFIRST BUTFIRST [FLOWERS HORSES MUSHROOMS]
Result: FALSE

EMPTY? BUTFIRST BUTFIRST BUTFIRST [FLOWERS HORSES MUSHROOMS]
Result: TRUE

Testing for the empty list is necessary when you want to use all of the elements in a list, one at a time. You know you're done when you find the empty list. The PRINT.DOWN procedure takes a list as input and prints it, one element at a time. It checks for the empty list in order to know when to stop printing. After the first element is printed, PRINT.DOWN is run with a slightly shorter list - the BUTFIRST of the original input list. PRINT.DOWN was defined earlier for words, but it works just as well for lists. Type:

PRINT.DOWN [[WILD FLOWERS] [WILD HORSES]]
WILD FLOWERS
WILD HORSES

Just like many Logo commands, you'll find that many of your own procedures work equally well with either words or lists.

The MEMBER? command takes two inputs and tests to see if the first input is a member of the second input. If a match is found, MEMBER? outputs TRUE, otherwise, MEMBER? outputs FALSE. The first input can be either a word or a list. Type:

MEMBER? [WILD HORSES] [[WILD FLOWERS] [WILD HORSES]]
Result: TRUE

MEMBER? "WILD [[WILD FLOWERS] [WILD HORSES]]
Result: FALSE

The word WILD is not an element of the input list - it's a member of a member of the input list. Checking the elements of a list within another list can be done recursively - it's just a bit more complicated than the recursion done by PRINT.DOWN. You not only have to test each element of the input list, you also have to check each element of the lists that are elements themselves. And, since a list can have a list as a member, the nesting can get quite deep. The ANY.MEMBER? procedure has more than one recursive call in it. In addition, it's purpose is to output either the word TRUE or FALSE, depending on whether or not the first input is a member of any member of the second input. Define ANY.MEMBER? and then work through the following examples.

ANY.MEMBER? "WILD [SOME WILD FLOWERS ARE WEEDS]
Result: TRUE

With a sentence as the second input, ANY.MEMBER? works much like the built-in MEMBER? command. It checks to see if the first input is equal to the FIRST element of the second input. If they are equal, then it outputs the word TRUE with the OUTPUT command, which stops the procedure. If they are not equal, then ANY.MEMBER? checks to see if the first element of the second input is another list. If not, then it just outputs the result of running ANY.MEMBER? with the BUTFIRST of the second input.

ANY.MEMBER? "WILD [[TAME HORSES] [WILD FLOWERS]]
Result: TRUE

If the FIRST element of the second input is another list, then ANY.MEMBER? is run with that list as the second input. However, the result of running ANY.MEMBER? can not be output immediately because, if the result was FALSE, the OUTPUT command would stop the procedure before it had the chance to check the elements of the BUTFIRST of the second input.

ANY.MEMBER? [WILD] [[WHAT [ABOUT [THIS [WILD]]]]]
Result: TRUE

ANY.MEMBER? works with either a word or a list as the first input, just like the built-in MEMBER? command. It just does a little more work when the second input has lists as elements. Have you noticed that the usual form of a Logo instruction is the command word followed by its inputs, if it needs any? This form is called prefix notation - the command word comes first. You can do every Logo command this way and it's easy to remember the format. Some command words look more natural if they go in between their inputs. This is called infix notation and the command words are called operators - in fact, most operators are really symbols, like =. Logo has both commands and operators for the list comparisons: equal and not equal. You can not compare lists to see if one is greater than or less than the other.

All of the comparison operators (represented by symbols) can be used in either a prefix or infix notation. The commands (represented by words) can only be used in a prefix notation. You can often avoid using parentheses to make Logo understand what you are doing if you use prefix notation instead of infix notation. Some examples will show you where parentheses are needed. It's not a big problem, as long as you're aware of it.

To see if two lists are equal to each other, you can use either of the commands = or .EQ or the operator =. Each of the following commands work the same:

EQUAL? [WILD FLOWERS] [WILD FLOWERS]
Result: TRUE

.EQ [WILD FLOWERS] [WILD FLOWERS]
Result: TRUE

= [WILD FLOWERS] [WILD FLOWERS]
Result: TRUE

[WILD FLOWERS] = [WILD FLOWERS]
Result: TRUE

You may need parentheses for the infix notation when the first (or left) input is more complicated than a simple list. It's often a very subtle problem.

EQUAL? BUTFIRST [WILD FLOWERS] [FLOWERS]
Result: TRUE

BUTFIRST [WILD FLOWERS] = [FLOWERS]
Result: ALSE

Result ALSE? What is that? The = operator has used the list [WILD FLOWERS] as its first (left) input and compared it to [FLOWERS]. Since they were not equal, it output FALSE which was then input to BUTFIRST which output all but the first character of FALSE which is where ALSE came from. In cases like this, you need to put parentheses around the first input to an infix operator or change the test to have the simple part as the first input.

(BUTFIRST [WILD FLOWERS]) = [FLOWERS]
Result: TRUE

[FLOWERS] = BUTFIRST [WILD FLOWERS]
Result: TRUE

You can reverse the meaning of a test with the NOT command. For the prefix notation, just put the NOT command in front of the comparison command word or symbol. If you used the infix notation, you can put NOT in front of the first (left) input. However, you don't really need to use NOT because Logo has all the comparisons you need. It's probably easier and clearer to change the command or operator.

You can read about the rest of the comparison commands and operators in the online help or the Reference Manual. They are an important part of programming. Combined with the TEST and IF commands, they add a sense of intelligence to your procedures.

Computing with Numbers

The numbers normally used by Logo are represented in the decimal number system, or base 10, which uses the digits 0 through 9. Fortunately, it's the same number system that people use all the time. You should know, however, that Logo can recognize numbers in other bases as well. For example, the prefix #B can be used to enter a binary number like #B1111, which is equivalent to the decimal number 15.

Logo displays normal decimal numbers unless you change the system variable $var[variables|BASE] to change the default display. Any base from 2 through 16 can be selected. If you need to use different bases, you can find out more in the online help or the Reference Manual. However, if you play around with $var[variables|BASE], be aware that regardless of what base you select, displaying the value of $var[variables|BASE] will always show 10. The right-most position of any number in any base is the one's position. The next position to the left is the ten's position for decimal numbers, but it's the two's position for binary, the eight's position for octal, and the sixteen's position for hexadecimal. It seems like a kind of computer math joke - when is 10 not equal to 10? When you use a different $var[variables|BASE] for each number. By default, Logo displays fractional numbers rounded to the nearest hundredths position - or, at most, two decimal places. You can change the system variable $var[variables|PRECISION] to control how many decimal places you want to see. All internal values have a precision of 15 decimal places regardless of the setting of $var[variables|PRECISION]. If you only want whole numbers to display, you can set $var[variables|PRECISION] to 0. The maximum setting is 15.

The leading zero of a number is not relevant to its value. For example, the numbers 0123 and 123 are equal to the value of one hundred twenty three. However, if you put a double quotation mark in front of a number with leading zeros, then the zeros will remain for display purposes only - it will not affect the value.

Numbers are every bit as important as words and lists. Knowing more about numbers can make your Logo experience more fun. You can count on it!

Taking Numbers Apart

Since numbers are also valid Logo words, all of the commands that work for words also work for numbers. The elements of a number are the individual characters that make up the number. The elements are usually the digits 0 through 9, but the decimal point and minus sign may also be elements of numbers.

The following examples show how numbers can be taken apart with the same commands that were used for words. The result is typically still a number. However, if you remove all of the elements of a number, you will end up with the empty word. The empty word is not a number.

FIRST 123
Result: 1
LAST 123
Result: 3
BUTFIRST 123
Result: 23
BUTLAST 123
Result: 12
BUTMEMBER 0 2000
Result: 2
FROMMEMBER 4 123456789
Result: 456789
PICK 123456
Result: 3
ITEM 3 123456
Result: 3
ITEM COUNT 1234 1234
Result: 4

The individual numeric digits also have an ASCII value, just like the other characters. Type:

CHAR 49
Result: 1

Thinking of numbers as words is a little different than thinking of them as numeric values all the time. Most of your work with numbers will likely be in calculations, but sometimes these manipulations can be handy, too. Suppose you wanted to test whether a 3-sided die is statistically equivalent to a 6-sided die that has duplicates of the three different numbers. A 3-sided die could be simulated with the number word 123 while the 6-sided die could be simulated with the number word 112233. You could use PICK 123 and PICK 112233 to simulate rolling the dice. Do you think RANDOM 3 is statistically equivalent? What if you want to use an “unfair” die - where one number appears more than the others? For example, PICK 172737475767 is more likely to roll a 7 than any of the other numbers. This would be more difficult to simulate with RANDOM.

Putting Numbers Together

Numbers can be put together with the same commands that work for words. The result is typically still a number. However, if you put two decimal numbers together, you will not have a number as a result.

WORD 12 34
Result: 1234
FPUT 1 234
Result: 1234
LPUT 4 123
Result: 1234
SUBST 1 2 1234
Result: 2234

Why would you want to manipulate numbers like words? Sometimes it's convenient to use a number as part of the name for a variable or an object, like a graphical control. For example, SCORE.1 and SCORE.2 could be the names of the statictext controls for displaying the total scores of players in a game. The variable CURRENT.PLAYER could represent the number 1 or 2, depending on whose turn it is. When you need to update the current player's score, you could use WORD “SCORE. :CURRENT.PLAYER to create the proper name of the control.

Comparing Numbers

All numbers are words, but not all words are numbers. If you need to check whether or not a word is a number, use the NUMBER? command. It outputs only if its input word can be interpreted as a numeric value; otherwise, it outputs . One way to use NUMBER? is in the TEST command. Type:

TEST NUMBER? 123

TEST is a rather special command. It doesn't output anything, but it does “remember” the result of the test in a variable called @COND. If you want to run some commands when the result of the test is true, you can use the IFTRUE command and give it a list of instructions to run. Type:

IFTRUE [PRINT [YES, THAT'S A NUMBER]]
YES, THAT'S A NUMBER

IFTRUE checks the value of @COND and if it's TRUE, the instructions in the list are run. The value of @COND is not changed until another TEST is done. You can put as many IFTRUE commands as you want after the TEST command. The opposite of IFTRUE is IFFALSE. Since the current value of @COND is TRUE, an IFFALSE command at this point would not mean anything. Type:

TEST NUMBER? "SEVEN
IFTRUE [PRINT "THANKS]
IFFALSE [PRINT [NUMBER, PLEASE.]]
NUMBER, PLEASE.

The IFTRUE command does not run its instructions in this example. IFFALSE checks the value of @COND and if it's FALSE, the instructions in the list are run.

An alternative to TEST is IF. In one form, IF is a combination of TEST, IFTRUE and IFFALSE.

IF NUMBER? "SEVEN [PRINT "THANKS] [PRINT [NUMBER, PLEASE.]]
NUMBER, PLEASE.

The first input to this form of IF is the same as the input to TEST - a condition that returns either the word TRUE or the word FALSE. The IF command does not make use of the @COND variable; it uses the result of the test to determine what to do next. The second input is the list of instructions to run when the condition is true. The third input is optional, but if you use it, the instructions in the list are run when the condition is false. Another form of IF is similar but does not have lists of instructions. Instead, it uses the special words THEN and ELSE as markers. When the test condition is true, the instructions after THEN and up to ELSE are run. ELSE is optional, so the instructions for THEN end at the end of the line if ELSE is not used. When the test condition is false, the instructions after ELSE and up to the end of the line are run. It's more like the way people say things when they make a decision. The following IF command is really the same as the example above:

IF NUMBER? "SEVEN THEN PR "THANKS ELSE PR [NUMBER, PLEASE.]
NUMBER, PLEASE.

The form of comparison you use is a matter of personal choice - one way works just as well as another. For more information, look in the online help and the Reference Manual.

The empty word has nothing in it - it's still a word but it is not a number. You test for the empty word with the EMPTY? command. It outputs TRUE if the word is empty and FALSE if it's not empty. If you remove all the characters of a number, you'll end up with the empty word. In the following example, use the up-arrow key and insert additional BUTFIRST commands after you try each line. Type:

EMPTY? BUTFIRST "123
Result: FALSE
EMPTY? BUTFIRST BUTFIRST "123
Result: FALSE
EMPTY? BUTFIRST BUTFIRST BUTFIRST "123
Result: TRUE
NUMBER? EMPTY? BUTFIRST BUTFIRST BUTFIRST "123
Result: FALSE

Testing for the empty word is necessary when you want to use all of the characters in a number, one at a time. You know you're done when you find the empty word. The PRINT.DOWN procedure from earlier works for words and lists; since a number is also a word, it works for numbers, too. Type:

PRINT.DOWN 54321
5
4
3
2
1

Sometimes you need to know whether or not a number is odd or even. Of course, there are ways to determine that with a calculation, but it's not necessary. Odd numbers end with one of the digits 1, 3, 5, 7, or 9. The MEMBER? command takes two inputs and tests to see if the first input is a member of the second input. If a match is found, MEMBER? outputs TRUE, otherwise, MEMBER? outputs FALSE. Type:

MEMBER? LAST 123 "13579
Result: TRUE
MEMBER? LAST 456 "02468
Result: TRUE

You could define procedures called ODD? and EVEN? to test for odd and even numbers.

The PIG.LATIN procedure you defined earlier should just output numbers with no change. Add the following test at the beginning of PIG.LATIN.

IF NUMBER? :INPUT THEN OUTPUT :INPUT

There are even more rules to Pig Latin - odd, isn't it? Have you noticed that the usual form of a Logo instruction is the command word followed by its inputs, if it needs any? This form is called prefix notation - the command word comes first. You can do every Logo command this way and it's easy to remember the format. Some command words look more natural if they go in between their inputs. This is called infix notation and the command words are called operators - in fact, most operators are really symbols, like =, <, and >. Logo has both commands and operators for the comparisons: equal, greater than, greater than or equal, less than, less than or equal, and not equal.

All of the comparison operators (represented by symbols) can be used in either a prefix or infix notation. The commands (represented by words) can only be used in a prefix notation. You can often avoid using parentheses to make Logo understand what you are doing if you use prefix notation instead of infix notation. Some examples will show you where parentheses are needed. It's not a big problem, as long as you're aware of it.

To see if two numbers are of equal value, you can use either of the commands = or .EQ or the operator =. Each of the following commands work the same:

EQUAL? 123 123
Result: TRUE
.EQ 123 123
Result: TRUE
= 123 123
Result: TRUE
123 = 123
Result: TRUE

You may need parentheses for the infix notation when the first (or left) input is more complicated than a simple number. It's often a very subtle problem.

EQUAL? FIRST 123 1
Result: TRUE
FIRST 123 = 1
Result: F

Result F? What is that? The = operator has used the number 123 as its first (left) input and compared it to 1. Since they were not equal, it output FALSE which was then input to FIRST which output the first character of FALSE which is where the F came from. In cases like this, you need to put parentheses around the first input to an infix operator or change the test to have the simple word as the first input.

(FIRST 123) = 1
Result: TRUE
1 = FIRST 123
Result: TRUE

You can reverse the meaning of a test with the NOT command. For the prefix notation, just put the NOT command in front of the comparison command word or symbol. If you used the infix notation, you can put NOT in front of the first (left) input. However, you don't really need to use NOT because Logo has all the comparisons you need. It's probably easier and clearer to change the command or operator.

You can read about the rest of the comparison commands and operators in the online help or the Reference Manual. They are an important part of programming. Combined with the TEST and IF commands, they add a sense of intelligence to your procedures.

Logo Math

Of course, Logo can do math - add, subtract, multiply, divide, and many other mathematical operations as well. People normally do math with infix notation. For example, type:

1 + 2
Result: 3

The + symbol is used for addition. When used in infix notation like this, it takes two inputs - one on the left and one on the right. All of the mathematical operators can be used in prefix notation as well. Type:

+ 1 2
Result: 3

And, each of the mathematical operators has a corresponding command word that only works in prefix notation. Type:

SUM 1 2
Result: 3

The prefix notation form of mathematical operators normally take two inputs. However, if you enclose the command or operator and its inputs with parentheses, then any number of inputs can be used. Type:

(+ 1 2 3 4 5)
Result: 15

(SUM 1 2 3 4 5)
Result: 15

The form of notation you use is your own choice. Sometimes the prefix notation is easier to work with. It may take a little getting used to, but it does have some advantages, especially when more than one operation is in an instruction line. For example, type:

1 + 2 * 3 + 4
Result: 11

The infix operators have an order of precedence that determines which operations are done before others. In this example, since multiplication has a higher precedence than addition, the multiplication of 2 * 3 was done first, giving a value of 6; then, the addition was done (1 + 6 + 4) which resulted in a value of 11. You can control the order of precedence with parentheses. Type:

(1 + 2) * (3 + 4)
Result: 21

Using the prefix commands, you can avoid the need for parentheses. Type:

PRODUCT SUM 1 2 SUM 3 4
Result: 21

If you cause an error message with a math instruction, Logo always uses the command word rather than the symbol in the text of the error message. Type:

"TWO / "ONE
QUOTIENT needs a number

You do have to type a little more with the prefix commands - subtraction is done with the - command. However, you can always create an ALIAS. Type:

ALIAS "SUM "ADD
ALIAS "SUM "PLUS
ALIAS "DIFFERENCE "SUB
ALIAS "DIFFERENCE "MINUS
ALIAS "PRODUCT "MUL
ALIAS "PRODUCT "TIMES
ALIAS "QUOTIENT "DIV
ALIAS "QUOTIENT "GOZINTA

Loopy Math

If you repeatedly add 1 to a variable, you would expect the value to grow larger and larger. That's a normal expectation, but sometimes it's necessary to control the range of a variable so that it is always within a particular range of values. There are many ways to do this, but using the REMAINDER command may be the simplest, once you understand what is going on with it. Type:

MAKE "STEP 4
REMAINDER :STEP 4
Result: 0

The REMAINDER command divides its first input by its second input and outputs the remainder of the division. In this example, the remainder is 0 because 4 goes into 4 just 1 time and there is no remainder. If you repeatedly add 1 to :STEP, it will get larger and larger, but the output of REMAINDER will not get beyond 3. Type:

REPEAT 5 [MAKE "STEP :STEP + 1 (PRINT :STEP REMAINDER :STEP 4]
5 1
6 2
7 3
8 0
9 1

The value of :STEP is getting larger but the output of REMAINDER stays in the range from 0 to 3. This kind of controlled sequence can be quite useful for “loopy” things that need to stay within a range from 0 to one less than the number of things. Of course, people would rather see a range starting from 1 to a maximum number. There is rather simple formula you can use that will keep a variable within a range from 1 to a maximum value. There are some initializations you need to make before you use the formula - assign the maximum value to the variable you want to use for looping and to another variable that you must never change (in other words, a constant data item). Type:

MAKE "MAX.STEP 4
MAKE "STEP :MAX.STEP 

:STEP is going to be used in doing loopy things. There is no such thing as a constant (unchangeable) data item in Logo, but the intent of :MAX.STEP is that whenever you need the maximum number of steps, you can always get it from :MAX.STEP. You'll see why :STEP is initialized to the maximum number of steps very shortly. Each time you need to use :STEP, you must first assign it a new value with the following command:

MAKE "STEP 1 + REMAINDER :STEP :MAX.STEP

The first time a new value is assigned to :STEP, the value will be 1 because the REMAINDER command will output 0. The next time a new value is assigned, it will be 2 because the REMAINDER command will output 1 - at this point, :STEP is 1, :MAX.STEP is always 4, and the remainder of dividing 1 by 4 is 1. Repeatedly running the MAKE command will make **STEP reach the maximum value and then “loop” back to 1 automatically. Type:

REPEAT 5 [MAKE "STEP 1 + REMAINDER :STEP :MAX.STEP PR :STEP]
2
3
4
1
2

If you create a procedure to do the assignment and output the new value, you can use the procedure name whenever you need the next value of :STEP. Define the NEXT.STEP procedure. It does not have any inputs - it's expecting to have access to :STEP and :MAX.STEP as global variables (like what you are using now). Of course, with dynamic scope, they could be defined in a superprocedure as well. Type:

REPEAT 5 [PRINT NEXT.STEP]
3
4
1
2
3

What about the sequences that start with 0? All you have to do is change the OUTPUT command to subtract 1 from the :STEP. Of course, you could subtract 1 from NEXT.STEP yourself. Type:

REPEAT 5 [PRINT NEXT.STEP - 1]
3
0
1
2
3

But, why not let your procedure do the subtraction for you? You might forget.

Math is really a fundamental part of working with any computer. Fortunately, most of it is rather incidental to doing fun things with turtles, bitmaps and drawings. Logo has a wide range of commands and operators that you can use to explore mathematics - from simple basic math to trigonometry and polar coordinates. If you're interested in mathematics, you can check out Logo's mathematical capabilities in the online help or the Reference Manual.

A Turtle Flight Plan

Turtles move in straight lines, but if you connect enough short lines together, you can make a turtle orbit the earth and do loopty-loops at the same time. All you need to do is file a flight plan before launching your turtle on its flight. (After you have defined the ten procedures in the illustration, of course.)

A flight plan is just a list of coordinate points put on the turtle's property list with the name FLIGHTPLAN. There are many different ways to make a flight plan. You can invent your own later on. MAKEPLAN takes three inputs: the number of points you want in the plan, the radius of the circle that encloses the flight path, and the direction in which the turtle should fly - either the word RIGHT or LEFT. The local variable :PLAN is used to create the list of coordinate points for the flight plan. As the new turtle named PLANNER is moved to each point, the coordinate point is added to :PLAN. A Logo command is just a word until you run it - PLANNER turns right or left, depending on the value of DIRECTION. When the plan is complete, PLANNER is erased and the flight plan is output. Type:

MAKEPLAN 4 100 "RIGHT
Result: [[0 100] [100 0] [0 -100] [-100 0]]

With just four points in the plan, the turtle would follow the path of a square - not a very likely flight path. However, it shows you what a flight plan looks like.

Filing a flight plan is just a matter of putting the plan on the turtle's property list. As you learned in Chapter 6 - Property Lists, it's best to create a procedure to manipulate the property list. TheFILEPLAN procedure takes a turtle number and a flight plan as input. The flight plan is filed as the value of the property name FLIGHTPLAN.

To file a flight plan for turtle 0, type:

FILEPLAN 0 MAKEPLAN 4 100 "RIGHT

To launch your turtle on its flight, just use FLIGHT. It takes the turtle number as input and gets the turtle moving. Type:

PENUP FLIGHT 0

The reason for doing PENUP first is that the turtle is not likely to be at the point of takeoff when you put it in flight. If you want to see a trail of the flight path, put the pen back down while the turtle is flying. The FLIGHT procedure uses LAUNCH to start the TAKEOFF procedure. The process identification number output by LAUNCH is saved on the turtle's property list so the LAND procedure can use it later when you want to stop the turtle.

The TAKEOFF procedure creates three local variables which are accessible by the other subprocedures that it runs. :PLAN is a copy of the turtle's flight plan. In a real flight plan, there are checkpoints along the route. :MAXPOINTS is initialized with the output of CHECKPOINTS, which counts the coordinate points in the flight plan. :POINT is used as a counter as the turtle moves from one point to the next.

The PRFLIGHT procedure just moves the turtle to the first coordinate point in the flight plan. Flying a turtle is just moving from one point to the next - just the type of thing that loopy math was made for. The FLY procedure does the loopy math with :POINT and :MAXPOINTS and then calls on NEXTPOINT to actually move the turtle to the next checkpoint. NEXTPOINT turns the turtle towards the checkpoint and takes one step at a time until the turtle arrives at the checkpoint coordinate (hopefully on time). The turtle continues to fly forever, until you request it to land. The LAND procedure uses the process identification number from the turtle's property list and uses it as input to the HALT command to stop the turtle.

To see a flight plan that has been filed, use GETPLAN. It takes the turtle number as input and outputs the flight plan. Type:

GETPLAN 0
Result: [[0 100] [100 0] [0 -100] [-100 0]]

What about the loopty-loops?. All you need is a different flight plan. The CAPTURE procedure is one example of making a flight plan by using the mouse. Once you start CAPTURE, you have two seconds to put the mouse pointer where you want to start. Hold the button down as you draw your flight plan. As long as the button is down, new (and different) coordinates are saved in :PLAN. When you release the button, CAPTURE outputs the flight plan. You can make some very complicated flight plans with CAPTURE. Not only loopty-loops, but you could write your name and use that as the flight plan. You'll have to be creative to dot your I's and cross your T's.

If you want a flight plan that just goes from “point A to point B” rather than looping repeatedly, you'll have to change the FLY procedure. You could use a FOR command to call the NEXTPOINT procedure for each coordinate and then stop. You can make the turtle move faster by changing the NEXTPOINT procedure. Try using either SETXY or FORWARD DISTANCE CHECKPOINT. This will make the turtle move much faster but not as smoothly.

To make a more realistic orbital flight path, try the MAKE.ORBIT procedure. It takes three inputs: the number of points you want in the flight plan, the horizontal and the vertical radius of the ellipse that outlines the flight path. The turtle stamps an ellipse with STAMPOVAL and then calls on GET.POINT which makes the turtle “walk” outward until it finds the line.

A circle drawn on a computer screen is really an illusion. The POLY.FLIGHT procedure creates five different flight plans, starting with a straight line of two points. As more and more points are added to the plans, the flight path gets closer and closer to a circle. Exactly how many sides does a circle have anyway? It depends on your resolution.

TO POLY.FLIGHT
	FILEPLAN 0 MAKEPLAN 2 100 "RIGHT
	FILEPLAN 1 MAKEPLAN 4 100 "LEFT
	FILEPLAN 2 MAKEPLAN 6 100 "RIGHT
	FILEPLAN 3 MAKEPLAN 8 100 "LEFT
	FILEPLAN 4 MAKEPLAN 10 100 "RIGHT
	TELL [0 1 2 3 4]
	EACH [SHOWTURTLE PENUP FLIGHT WHO]
END
logo/tutorial/chapter10.txt · Last modified: 2019/01/22 06:09 (external edit)