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 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 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
- 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