|
Introduction
Variables |
|
But there are two characteristics of inputs which limit what we can do with them:
In this lesson, you will learn about a new kind of variable that can be declared inside your procedures and a kind of variable which can exist outside of any specific procedure. In computer science jargon, the accessibility of a variable (where it can be accessed) is called the scope of the variable.
I'm going to start off with an example where a local variable is used to save the day - to fix a bug in a simple drawing program. This is followed by walking through the process of writing a program which is a bits-vs-atoms copy of some art stuff I did as a child - stringart. In it, a local variable is used similarly to the way one was used in the first example. Then, I'll get you started on an exercise where you can extend the tree program you wrote in the last lesson. With a local variable, you can draw trees that are much more real looking.
In the second half of the lesson, I introduce the concept of a global variable. We use global variables in the final project in this lesson - a simple four-function calculator.
Inputs are variables that are created and filled with values when a procedure is invoked. The names of the variables, are given on the procedure's title line. These identifiers can be used in the instructions which make up the body of the procedure. Inputs are also called local variables - they are local to the procedure in which they are defined.
Just to refresh your memory, given:
to box :size
repeat 4 [ forward :size right 90 ]
end
box 100
The first three lines are the definition of the procedure box
which includes the input identifier :size. A value is
placed in the variable named "size" when the procedure box is
invoked. In this example, it is filled with the value 100
by the invocation, the last line.
Question: what if you need a variable that holds it's value across procedure boundaries and/or requires a lifetime that's longer than the execution of one procedure?
Think about how a calculator works. You are going to be creating one later in this lesson. You will need a variable that holds the current number as it is entered - one mouseclick at a time. Since the mouseclick procedure will be executed multiple times, the variable needs to be declared independent of this one procedure. If you are confused or this is just too hard to imagine, checkout the calculator applet later in this lesson. Then, reread this paragraph.
Answer: you use the global command to declare a variable that exists outside the text boundaries of all procedures.
global commands are included in your source code like to commands (procedure title lines) that introduce procedures, at what's called the outermost level. It is best to put all of a program's global commands first.
Here is a summary of the important properties of variables:
Ok, lets work through an example that shows a use of the local command.
| Example 11.1 |
So, what you do is click the mouse in the window where you want the turtle to move to. In the process, if the turtle's pen is down, a line is drawn from where it was to where the mouse is clicked. There are two buttons, one to clear the window (start over) and another that lets the user lift or lower the pen - its action flips back and forth based on the current state of the pen. Play with the applet a bit to get a feel for it...
You know how to do almost everything needed to write this program. So, click here to get most of the code needed. You can copy/paste it into TG.
Check it out. It has a bug in it. You can move the turtle around by clicking the mouse at various spots in the window, but when you click on the PenDown button, the turtle is moved to the button. This is not what we want... Read through the code, what's wrong? How can we fix the problem?
Debugging a program is done in steps that match the Scientific Method.
Starting with observation, if you play with the program, you can get a few clues as to what the bug is. The Clear button works fine. But, when you click on the PenUp/Down button, the turtle moves to the button instead of remaining where you've chosen to start drawing a line. If you click mouse outside of the PenUp/Down after clicking on it to lower the pen, you'll see that the line starts at the base of the letter "P" of the "PenUp" text. So, our hypothesis is that the program is moving the turtle to redraw the button with new text (changing "PenDown" to "PenUp") and leaves it there - our bug!
You have multiple ways of going through the last to steps: predicting and then testing your prediction. What I would do is read the program carefully to try to find what needs to be changed. But another way is just to search the program for the "PenUp" text. There are a few penup commands in the program, but only one "PenUp word, it has the matching uppercase "P" and "U" characters in it. It is in the lowerPen procedure, which is simple to read. sure enough... it redraws the PenUp/Down button to change its label to "PenUp. When this is done, the turtle needs to be moved to do this. And, there is no instructions in lowerPen to get the turtle back to where it was!
So, our prediction is that the turtle is being moved down to draw the button and not being put back. To test this, you can add some debug code. What you need is a way to print out where the turtle is. If you read through the TurtleTalk Primitives Appendix (Graphics Procedures) you can find a few operators which do the job. Checkout the pos, xcor, and ycor procedures. They are operators that output coordinates for the current position of the turtle. You haven't learned about sentences yet but this is debug code so who cares, let's use the pos operator to print where the turtle was coming into the lowerPen and where it is at the end of the procedure. Modify lowerPen to look like this:
to lowerPen
println pos
drawButtonAt pudButtonX buttonsY "PenUp
pendown setpencolor black
println pos
end
Then invoke init in the CommandCenter to restart the program. Click the mouse on the PenDown button. two lines of numbers are printed in the CommandCenter, the position of the turtle when lowerPen started and where it ended up at. Then lines of numbers don't match! Our test step has proved our prediction and so our hypothesis is probably correct.
If you want to prove to yourself that the turtle has been left in the button you can check to see what the coordinates of the button are. If you look at the drawButtonAt procedure, you'll see that its first two inputs are the X and Y coordinates of the lower left corner of the button drawn. And reading more code, you'll see that the text in the button is four turtlesteps right and eight turtlesteps above this point. Go back to the CommandCenter and type in println commands to see what pudButtonX and buttonsY output. This should help prove your at the lower left corner of the PenUp text.
Figure 11.1 shows TG after performing the test.
|
| Figure 11.1 |
So, to fix the bug you need to know about two new commands: local and make.
The local command declares a new variable for use inside a procedure. Like an input variable, it only can be referenced inside the procedure containing it. The syntax of the TurtleTalk local command is:
| local | <name> |
Examples of local instructions are:
local "identifier
local "savePos
Notice that the input identifiers are
words in these examples
instead of having dots as their prefix (:identifier or :savePos).
You are probably thinking... "Why is there a quotation mark prefixing the variable's name instead of a colon (dots)?" Input variable identifiers on a procedure title line are always prefixed with dots, e.g.,
to box :size
Well, this title line could also be written
to box "size
although you rarely see it entered like this. A procedure title
line is special. It does not follow the rules that the rest of
the commands go by. But, since you will probably never see a Logo
procedure title line with quoted names, I'll stick to dots-prefixed
identifiers in all my examples.
The make command puts a value into a variable. The syntax of the TurtleTalk make command is:
| make | <name> | <value> |
We can use these two commands to save the turtle's current position at the start of lowerPen. These commands will replace the first debug instruction.
But, how do we use the value produced by pos to move the turtle back after drawButtonAt has completed? Again, looking in the TurtleTalk Primitives Appendix (Graphics Procedures) reveals a command called setpos which takes an input that is a sentence like produced by pos. After the drawButtonAt instruction, we will make sure the pen is up and use setpos to move the turtle back to the saved position. So our modified lowerPen now is:
to lowerPen
local "savePos make "savePos pos
drawButtonAt pudButtonX buttonsY "PenUp
penup setpos :savePos
pendown setpencolor black
end
Make the changes in the Editor and try it out. It works... Well, almost... The first time you click on the PenUp button, the turtle stays on it!!! Ahhhh!!!!
Well, to make a long story short, if you read more of the program you will see that you need to make the same changes to the procedure that is the counterpart to lowerPen. Its name is liftPen.
OR... what we can do is move saving and restoring the turtle's position into the drawButtonAt procedure. This is the best way to solve our bug. Why? Because we will shortly want to add more buttons to our program. How about a button to change the color of the turtle's pen? How about a button to change the size of the turtle's pen? How about a Fill button?
So, why add the same code to all of these procedures? Instead we should solve it once, in drawButtonAt.
Go ahead and put lowerPen back to its original text and improve drawButtonAt by changing it such that it saves the current location of the turtle as the first thing it does and then ends by restoring it.
So, now that we have computers and TurtleGraphics, we'll draw some StringArt. In the process, you might learn something about geometric design. If you are already a math wizard, you could use your understanding of trigonometry to write a StringArt program. But, don't worry if you haven't been introduced to trig yet! I'm going to show you how to use global variables, to produce StringArt. All you need is the depth of expertice in mathematics that you've used to get this far. You'll first draw something like Figure 11.2a and then your only limitation will be your own imagination.
In the following paragraphs I use common naming/terms from a junior high school math textbook (Gateways to Algebra and Geometry), e.g., angle, ray, vertex, etc... If you need background in this area, there is a ton of material out on the web, just Google for it. Here is a good introduction to angles that I found: http://www.mathleague.com/help/geometry/angles.htm
|
|
| (A) | (B) |
| Figure 11.2 | |
The basic technique in StringArt is to identify corresponding points on the sides of angles in some geometric object and then connect them with colored string. On the sides of angle QPR in Figure 11.2b, three points are marked. On one ray, line segment PQ, the numbers start at 1 for the closest point to the vertex and increase as you move away from it. On the other ray, line segment PR, the numbering is reversed. When identically numbered points are connected by straight lines, a hyperbolic curve is formed. These curves are pleasing to the eye and structurally sound; Google "Felix Candela" to see how he used hyperbolic curves in his beautiful architectural works in Mexico.
Our exercise is to write a procedure which is given
Figure 11.2a was drawn with its vertex, point P, at -100,-50. A line segment (PA) of ray 1 has a heading of 30 degrees in TurtleSpace and is 200 turtlesteps long. A line segment (PC) of ray 2 has a heading of 110 degrees and is 200 turtlesteps long. It has 11 strings.
I promised above that you would not need to know trigonometry. Here's how we'll get around this: we'll get the turtle to walk along the path of Ray-1 to a point. The current position of the turtle, its X and Y coordinates, can be obtained using the pos operator. We will remember them in a local variable. Then we'll return to the starting point and walk along Ray-2 until we get to the corresponding point on it. Once there, we instruct the turtle to lower its pen and go to the remembered point with the setpos command.
Here's a start; it's incomplete but should give you a feel for the structure of the solution:
to black output 0 end to blue output 1 end ; draw string :n of :numPts strings ; a string connects corresponding points on line segments PQ and PR ; point P, the vertex of angle QPR, is at :x,:y ; PQ has a heading of :pqHed degrees and is :pqLen steps long ; PR has a heading of :prHed degrees and is :prLen steps long to drawString :n :numPts :x :y :pqHed :pqLen :prHed :prLen penup setxy :x :y setheading :pqHed ; walk to current point on ray-1 ; remember point where the turtle is setxy :x :y setheading :prHed ; walk to current point on ray-2 ; draw to the remembered point end ; iterate through drawing :numPts strings to angleStrings :n :numPts :x :y :pqAng :pqLen :prAng :prLen ; stop-rule drawString :n :numPts :x :y :pqAng :pqLen :prAng :prLen ; recursive invocation (do next point) end to stringart :x :y :pqAng :pqLen :prAng :prLen :numStrings :color setpencolor black setpensize 4 penup setxy :x :y pendown setheading :pqAng forward :pqLen back :pqLen setheading :prAng forward :prLen back :prLen setpencolor :color setpensize 1 angleStrings 1 :numStrings :x :y :pqAng :pqLen :prAng :prLen end to main hideturtle home clean stringart -100 -50 30 200 110 200 11 blue end main
You've learned everything you need to extend this to arrive at a working program. Go off and give it a try... At least play around a bit.
Did you get it working? If so, great job! Skip ahead to More Realistic Looking Trees. If not, don't fret, you'll get it; let me give you some tips.
My RULE #1 is: "get something working, then improve it."
So, let's get a single line drawn. Let's write two procedures: walkToPointFromA and walkToPointFromB where AB is a line segment. The procedures have the inputs: :pointNum, :abLen, and :numPts. The difference between the procedures is the ordering of the points, see Figure 11.3.
|
| Figure 11.3 |
; Walk to a point on a line segment AB. ; The points are numbered from A towards B. ; The closest point to A is point 1, the next closest is point 2, ; the next, next closest is point 3, etc... to walkToPointFromA :pointNum :abLen :numPts ;... end ; Walk to a point on a line segment AB. ; The points are numbered from B towards A. ; The point furthest from A, closest to B is point 1; ; the next furthest is point from A is point 2, etc... ; the next, next furthest is point from A is point 3, etc... ; The point closest to A is point number :numPts to walkToPointFromB :pointNum :abLen :numPts ;... end
With these completed, you can plug invocations to them into drawString, i.e.,
to drawString :n :numPts :x :y :pqHed :pqLen :prHed :prLen penup setxy :x :y setheading :pqHed walkToPointFromA :n :pqLen :numPts ; remember point where the turtle is setxy :x :y setheading :prHed walkToPointFromB :n :prLen :numPts ; draw to the remembered point endOff you go; fill in the code for the walkTo... procedures.
A good next step is for you to fill in the missing code that saves the turtle's position and then goes to that position once you've moved to the corresponding point. You've been introduced to the local, make, pos, and setpos commands. For remember point, we can use local and make to put the turtle's position into a local variable. This variable will then be accessed when we instruct the turtle to go to the remembered point.
You could also use the setxy command together with a couple of other operators which you haven't used yet (xcor and ycor) instead of pos and setpos. In this case you would declare two local variables, one to hold the X coordinate and the other to hold the Y coordinate. It's up to you - either way works.
to drawString :n :numPts :x :y :pqHed :pqLen :prHed :prLen penup setxy :x :y setheading :pqHed walkToPointFromA :n :pqLen :numPts local "rememberedPoint make "rememberedPoint pos setxy :x :y setheading :prHed walkToPointFromB :n :prLen :numPts pendown setpos :rememberedPoint end
If you have everything right, your program should now draw the angle with the first string. If not, it's debug time. Use trace to see if your procedures are getting inputs that match what you think they should. Add println instructions to see what is happening when your program is being executed. Treat debugging a program as a puzzle, a game - so solve it!
After you get this working, then finally tackle the problem of iterating through all of the points. Review the last lesson where you learned how to use recursion to do this if you need to. All you need is to get your point number input which to go from 1 to the last point number.
Keep at it till you have a working program. You can do it.
Remember that you learn more when you make mistakes and fix them.
More Realistic Looking Trees
|
|
| Figure 11.4 | |
So, we had fun taking advantage of the power of recursion in the last lesson drawing trees. With just eight instructions, we could draw trees that could have any number of branches we wished. But, they were so, well... computer generated. The turtle is is a digital turtle, not alive, composed of atoms, and so it draws very straight lines. Let's teach it to not be so perfect.
First, let's write a procedure that draws a rough line, a sketched line, and use it instead of forward and backward.
We are going to write a procedure named sketchLine that moves the turtle forward some amount of turtle steps. When it's done it will end up at the same spot that a forward command would, given the same number of turtle steps as its input. But, the turtle will wander around a bit on its way to the spot.
If you think you know how to do this, go ahead and write a procedure on your own. If you don't know how to start, stay tuned...
Global variables
For your TurtleTalk programs, it is best to put your global variable declarations at the top/beginning. This way, you can see, in one place, the names of variables that will be referenced across procedure boundaries. Since a declaration creates an empty container, you need to use a make command to put an initial value into it. Since all programs should have an init procedure that does initialization, this is the best place to put these make instructions.
Let's look at an example of how you could use global variables...
In the lesson Defining Your Own Operators, the first thing I showed you how to do was write procedures that did nothing but output a number. This was a form of abstraction, where I gave names (that mean something to me) to numbers used inside the computer to represent colors.
In past examples, I have defined procedures black, blue, etc... that simply output the colors' numbers. Then, using these procedures as the input to a setpencolor command made reading the code easier to understand. As an example:
to red
output 4
end
setpencolor red
Global variables can be used to achieve the same results. Here is part of a program that shows the use of a bunch of global variables..
global "black
global "green
global "red
global "signalX
global "signalY
global "signalRadius
to drawSignal :action
pu setxy :signalX :signalY
setc :black
repeat 360 [fd :signalRadius pd fd 2 bk 2 pu bk :signalRadius rt 1]
if equal? :action "go [setpencolor :green]
if equal? :action "stop [setpencolor :red]
fill
end
to init
hideturtle home clean
make "black 0
make "green 2
make "red 4
make "signalX -50
make "signalY 0
make "signalRadius 25
end
to main
init
drawSignal "stop
wait 2000
drawSignal "go
end
main
So, what's going on here? Well, as soon as a global instruction
is interpreted, an empty variable is created;nbsp; The name of this variable
is available anywhere in the program. This is the reason that the first
thing that you should do in a program is to declare all of it's global
variables.
The syntax of the TurtleTalk global command is:
| global | <name> |
Here is the TurtleGraphics applet. Play around a bit. Declare a few global variables, try accessing them before you use a make command to give them their initial values, use make to set their initial values, and then access them. Write a procedure which has inputs (e.g. boxAt :x :y :size) and notice that once you type in the end word which terminates your definition of boxAt, you can no longer reference its inputs. Type in the example I gave above to see what it does, make sure you understand it.
| TurtleGraphics Applet |
The use of names in place of numbers in your programs is always a good thing to do. I use names for the coordinates of objects that I draw in TurtleSpace all the time, like buttons in the user interface part of a program. This allows me to change them in one spot instead of searching for where the object is drawn, where I test to see if the mouseclick was within its bounds, etc...
Many computer languages have explicit support for global variables that, once assigned a value, can not be changed.
A Four-Function Calculator - How It Works
|
| Figure 11.5 |
Figure 11.5 shows the calculator we're going to build. It has space for 16 keys and a display. There are ten digit keys used to enter numbers and five operation keys which determine what the calculator does.
As the mouse is clicked on number keys, the current value being accumulated and displayed is multiplied by 10 and the key's value is added in. As an example, when the program starts, the current value is zero and when a number key is clicked on, the current value becomes the value of the key. Let's say the "1" key is clicked on and the current value becomes 1. If the mouse is then clicked on another number key, say "2," the new current value becomes 12 ( 1 * 10 + 2 ). If the mouse is then clicked on the "3," the current value becomes 123 ( 12 * 10 + 3 ).
|
| Figure 11.6 |
Figure 11.6 shows our calculator after a user has clicked on the "1" key, the "2" key, the "3" key, the "4" key, the "5" key, and the "6" key.
Next we have the action keys. The action keys: "/," "X," "-" and "+" do two things.
Continuing the example, Figure 11.7 shows our calculator after the user has clicked on the subtraction key ("-") and then on the "4" key, the "5" key, the "6" key, the "7" key, the "8" key, and the "9" key.
|
| Figure 11.7 |
And, finally, Figure 11.8 shows our calculator after the user has clicked on the compute-answer key ("=").
|
| Figure 11.8 |
I'm doing this because I want you to concentrate on learning about how/why global variables are used in a program - not how to design a calculator. Since you are still learning the basics of programming, I want to offer you one way of structuring your program that I know will work. I'll cover how to design the structure of a program in a future lesson.
Figure 11.9 shows the most important procedures that make up my initial design of the calculator program. The arrows indicate invocations of procedures. The program is started by invoking the init procedure. After this, all execution is driven by mouseclick events (invocations of the procedure mouseclick), which occurs when the user clicks the mouse in the graphics area where the calculator has been drawn.
|
| Figure 11.9 |
Looking at Figure 11.9, you see that my calculator program consists of some procedures you are already familiar with. You've written procedures similar to frameAt, coloredRectAt, and mouseInRect?. The procedures digitAt, keyAt, displayMinusSign, and drawKeypad provide higher-level functionality - they draw the parts of the calculator using the primitive procedures. You are familiar with generic stuff that mouseclick and init need to do. It was covered back when we put together our first User-Interface if you need to refresh your memory.
I'm going to give you most of my code for the calculator so that we can spend our time on the interesting procedures. I'm including stubs (empty procedures) for these procedures so that you can copy-paste the code into the TG application.
Most of my calculator program - as a starting point. .
The second headstart I'm giving you is enough of the layout of the graphics part of the program. By now you have drawn so many boxes that it's got to be getting pretty boring. And, figuring out the size of things like the keys and the labels takes a lot of time.
Figure 11.10 shows a partial layout of the calculator in TurtleSpace.
|
| Figure 11.10 |
Like the named constants and stringart examples, we need to use the
global command to create our variable. I named my variable
curNum and placed the following line at the top of my source code.
Read the Code You've Been Given
The very first thing you need to do is read the code you have.
in the real world, in most software development positions, modifying
existing programs is what's done most often. Scanning over the
existing code is important for at least two reasons:
displayNum - Display a Number
Okay, let's get going. The first thing we will do is to get
our calculator to display a current number. Now we need a
global variable. The concept of a current number is
a real need for one. It's value will change as the user clicks on
digit keys and the equals key.
global "curNum
Now that we have a curNum variable, we need the init procedure to
initialize and display it. Here is the line I added to init
to set curNum to an initial value of 0.
make "curNum 0
Look back at Figure 11.9 (the structure
of my program) and the code for the
calculator. init invokes displayNum with the
number it wants displayed as an input. So, in our case, we want to
display the contents of the global variable curNum. So,
we replace the comment line ";display the current..." with an
invocation of displayNum.
displayNum :curNum
And what does displayNum do? I'm going to help you with the
math stuff and the locations for the things you're going to draw.
Figure 11.11 gives you many of the coordinates for the minus sign
that indicates a negative number and the six digits that are displayed.
With the labelheight of 36, the characters look good when spaced
25 turtle steps apart on the X axis.
|
| Figure 11.11 |
As for the math stuff, you are going to need to use three new operations: minus, remainder, and round.
To reduce the complexity of you first version of the calculator, it does not support a fractional part. So, you need to know how to get rid of it before you attempt to display a number. Even though the user can not enter a number with a fractional part, one can be generated with division, e.g., divide 100 by 3. The round operator takes one input and outputs the closest integer to it. The first thing that displayNum needs to do is make sure that curNum is an integer.
When you read the base code I gave you, hopefully you noticed the procedures displayMinusSign and digitAt. If not, check them out. They are key to how you will get a number displayed.
Determining whether or not to display the minus sign is easy. If the input's value is less than zero, you need to display the left-justified minus sign, the dash character. An operator you need to know about is the minus operator. It takes one input and outputs the negative of its input, essentially changing the sign of its input.
The challenging piece of displayNum is how to display a right-justified number. As with most programming problems, there is more than one way to do it. One way would be to determine how many turtle steps long a single-digit number is, a two-digit number is, etc... Then with a bunch of if commands, you can position the turtle at the right spot in the display area, and output :num with a label command. I didn't want to go to this much trouble.
I decided to figure out the width of a single digit and then display each digit separatedly - with the digitAt procedure. So, my problem became one of computing the value of each digit. My approach was to first determine whether or not each digit needs to be displayed for the given :num. As an example, unless :num is greater than 9, all I need to display is the rightmost digit. But, if :num is greater than 9 then I know I need to display a value in the tens column. If :num is greater than 99 then I need to display a value in the hundreths column. Etc... Here is an example of one of my if instructions.
if greater? :num 9 [ digitAt 40 115 isolated-tens-digit ]
But how do I get the isolated-tens-digit. Well, you need
an operator I haven't covered yet - the remainder operator.
| REMAINDER | number1 number2 | Outputs the remainder left after dividing number1 by number2 | REMAINDER :num 10 |
The remainder operator with :num as its first input and 10 as its second input will produce an output equal to the least significant digit of :num. The code for displaying the one's column of :num is:
digitAt 65 115 remainder 10
Combining the remainder operator with the quotient operator isolates any digit of a number.
Example: Display the isolated digits of the number 4321
println remainder quotient 4321 1000 10
println remainder quotient 4321 100 10
println remainder quotient 4321 10 10
println remainder 4321 10
Go back up to the TG Applet and type in
the above example. Play around a bit until you see/understand
what is going on...
You can now complete the if instruction given above by exchanging the isolated-tens-digit piece with code that does just that...
OK... I've given you enough hints. It's up to you to complete your displayNum procedure. When you complete it, test it out. Invoke init and see if your calculator now looks like Figure 11.6.
The digit just clicked on becomes the least significant digit of the current number - simply multiply the current number by 10 and add in the new digit. You use the make command to give the current number a new value. Here you go.
to digit :num
make "curNum sum :num product :curNum 10
end
I've given you this make command so that I can point out and
explain three things.
Oh... there's one more thing that digit needs to do. I'll let you figure it out.
The Math Function Keys signal two things.
At this point when I was writing my version of the calculator, I added two more global variables. I created a prevNum variable which holds the previous number and a mathOp variable which holds the chosen mathematical operation.
As an example, my plusKey procedure sets prevNum to the value of the current number, then sets the current number to zero. Finally, it puts the word sum into mathOp.
The other math function keys are the same except for the value stored into mathOp.
I'll get you started by giving you one line in my equalsKey procedure.
to equalsKey
if equal? :mathOp "sum [ make "curNum sum :curNum :prevNum ]
end
And the rest of the procedure is left for you... enjoy!
You've learned:
For conditional execution you need to make sure you have the predicate right. If not, when you think the list of instructions should be executed, they may not be.
For iteration, when you write recursive procedures, you need to make sure your stop-rule is right, and that your recursive-invocation changes an input variable one step closer to what the stop-rule is looking for.
And now, with global variables, you need to consider what is going on
across procedure boundaries. One procedure depends upon another
to do its thing - they work together.
Exercises
Oh, you'll also need a way to remove the leading minus sign from the number. Since it's displayed by displayMinusSign, you need to convert the negative number into a positve one. There is a primitive operator, minus that outputs the negative of its input. Applying the minus operator on a negative number gets you its positive value...
Modify your displayNum procedure to use count, minus and a single label command to display the digits.
Summary
Global variables are containers that are created with the
global command and can be accessed anywhere in your program.
One gotcha is that the global command that introduces the
variable (called its declaration in TurtleTalk) must be interpreted
before its identifier can be entered in an instruction.
Most introductory programming classes/texts introduce global variables much sooner than I have. I've worked on lots and lots of code in my days as a programmer and I've seen too many inappropriate uses of global variables. Their usage (I guess I should say their misuse) is a common source of bugs. Global variables are a very important, powerful concept in programming. But, use them responsibly.
To read more about trouble you can get into with global variables, Google "global variables considered harmful" and check out the pages that come up. William Wulf and Mary Shaw wrote a paper a long time ago that is still referenced/debated today.
| New TurtleTalk Procedures Used In This Lesson | |||
| Name | Input | Description | Example |
| GLOBAL | name | Declares a global variable named name. The variable can be referenced anywhere in the program's text following this command. The variable has no initial contents. | GLOBAL "var |
| LOCAL | name | Declares a local variable named name. The variable is local to the procedure it is in. The variable has no initial contents. | LOCAL "var |
| MAKE |
name value |
Creates a variable named name if it doesn't already exist. The contents of the variable is set to value. | MAKE "WHITE 7 |
| MINUS | number | Outputs the negative of number | MINUS 122 |
| POS | Outputs the current coordinates of the turtle as a sentence. The X coordinate is the FIRST of the sentence; the Y coordinate is the LAST of the sentence. | MAKE "CURLOC POS | |
| REMAINDER | number1 number2 | Outputs the remainder left after dividing number1 by number2 | REMAINDER 17 3 |
| ROUND | number | Outputs the closest integer to number | ROUND 22.45 |
| SETPOS | sentence | Moves the turtle to an absolute position, specified by a sentence containing two numbers. The two numbers are (FIRST) a new horizontal (X) coordinate and (LAST) a new vertical (Y) coordinate. | SETPOS [100 100] |