COMP203: Lecture 6


Syllabus | Homework and Assignments | Grading Rubric | Midterm Exam | Final Project

When circumstances allow, I'll be typing up bits of my lecture notes and posting them online. These may or may not bear any resemblance to the actual lectures.

Scope of Variables

Let's go back to the picture we made using squares:
to square :sidelength
 repeat 4 [forward :sidelength right 90]
end

to house                                                               
 square 100 ; frame
 right 90 forward 50 left 90 ; move to door
 square 30 ; door
 penup forward 50 left 90 forward 20 right 90 pendown ; move to window
 square 15 ; window
 penup back 50 right 90 back 30 left 90 pendown ; move back to start
end
The instructions in the procedure input several different values to the command square. This means that the variable sidelength will have different values at different times.

Thinking back to our comparison of Logo variables to algebraic variables, it isn't surprising that we want to "evaluate square for different values of sidelength". Each time we run procedure square with a new input, Logo puts the value of the input in the location sidelength, and when square has finished its job Logo forgets that value of sidelength and in fact forgets that variable sidelength ever existed -- it "frees up" the storage space that it had "allocated" for variable sidelength. This is a little bit like what the Post Office does when a person stops using a PO box.

From page 51: Variables that belong to a procedure are temporary. They exist only so long as that procedure is active.

This important pronouncement goes on to say: If one procedure has a variable with the same name as one belonging to its superprocedure, the latter is temporarily "hidden" while the subprocedure is running.

That last bit means that if house has a variable named sidelength then while square is running, the only variable sidelength Logo can use is procedure square's variable sidelength. It's as if Logo forgets about house's sidelength while square is running.

Here's an experiment we can run that might help us understand this:

to testvalue :sidelength
 print sentence [In procedure testvalue, :sidelength is] :sidelength
end
? make "sidelength 50
? print :sidelength
? testvalue 100
? print :sidelength
While procedure testvalue is running, the value of variable sidelength is 100 (or whatever was input to testvalue). Before the procedure starts and after the procedure finishes, the value stored in sidelength is 50 (or whatever we set it to using make).

Dynamic Scope

We've seen what Logo does when a procedure and its helper procedure both use the same variable name. Pages 49-50 of Chapter 3 describe what happens when a procedure uses the name of a variable that's not defined in that procedure.

to pentagon
 repeat 5 [forward :sidelength right 72]
end
The procedure pentagon uses a variable sidelength that is not defined anywhere in that procedure. What will happen if you run pentagon? Maybe you get the error message "sidelength has no value in pentagon" or maybe Logo draws a pentagon.

What determines whether you get a pentagon or an error message? When procedure pentagon tries to run the command forward :sidelength, Logo has to figure out what the value of sidelength is. On page 50 of chapter 3, we learn that:

If a procedure refers to a variable that does not belong to that procedure, Logo looks for a variable of that name in the superprocedure of that procedure.

When Logo tried to evaluate :sidelength and couldn't, it looked for a variable sidelength in the procedure that used pentagon as a helper procedure. If we had defined sidelength at the command prompt (while we were discussing squares), Logo found that previous value and used it. If we had not, Logo did not find that value and gave us an error message.

To see what value sidelength has when you're at the command prompt, use the instruction:

print :sidelength
If the value stored in sidelength is a number, procedure pentagon will work; otherwise it will not.

 

The procedure below draws a 45-45-90 right triangle.

to rtriangle :size
  forward :size right 90 forward :size     ; two legs
  right 135 forward product :size sqrt 2   ; hypotenuse
  right 135                                ; return to original facing
end
Three dimensional computer animations, such as those appearing in video games, are generally the result of the computer displaying a large number of triangles on the screen. If we were going use rtriangle that many times, it would be much faster to pre-compute the square root of 2 rather than computing it each time rtriangle runs. We can use a global variable and dynamic scope to store the output of sqrt 2 in a variable named sqrt2 and use it in the procedure.
make "sqrt2 sqrt 2
print :sqrt2

to fastrtriangle :size
  forward :size right 90 forward :size     ; two legs
  right 135 forward product :size :sqrt2   ; hypotenuse
  right 135                                ; return to original facing
end
If a procedure refers to a variable that does not belong to that procedure, Logo looks for a variable of that name in the superprocedure of that procedure.

In this example, there is no superprocedure for fastrtriangle so Logo looks for a global variable named sqrt2.

The text has a more complicated example that illustrates how this rule works with sub- and super-procedures. Load the procedures top and bottom defined on page 50 of Chapter 3. Notice that top has two inputs, variables inner and outer. Procedure bottom is a helper procedure with one input, variable :inner.

Remember that while procedure bottom is running, when Logo looks for a value for :inner it will find the one input to bottom, not the value :inner has in procedure top.

On the other hand, procedure bottom does not define a variable :outer. When Logo tries to find the value of outer in the second line of procedure bottom it will ask its superprocedure top if it knows the value of inner. Procedure top does have a variable named inner, and its value is given to bottom and input to sentence.

Note: What the scope of a variable can be in a programming language is a decision made by the people who design that language. The designers of the Logo language could just as easily have decided that since inner is not defined in procedure bottom, it should have no value at all in procedure bottom. Different programming languages have different rules for scope of variables.

Global Variables

Global variables are defined at the command prompt using make (like sqrt2 and sidelength were) and are accessible to every procedure that runs in Logo.

If we change the value of the global variable sqrt2, procedure fastrttriangle stops working. Because procedures inherit variables from their superprocedures, any procedure can change the value of sqrt2 deliberately or by accident. This could lead to a situation in which procedure fastrtriangle works until some other procedure is run and then mysteriously stops working -- remember the example of procedure pentagon. Such errors can be very hard to debug!

Having a procedure you're working on now succeed or fail depending on what you did half an hour ago is a bad situation to be in. For this reason, we try to avoid using global variables. A good rule of thumb is to only define global variables for values that never change: e.g. pi or the square root of 2. Using a global variable to store a date, name, length, or other information that might differ between procedures can lead to trouble.

Writing New Operations

So far we know how to write commands (procedures that do something for us) but not operations (procedures that do something for other procedures). We saw a few examples of how to define an operation earlier; on page 48 of Chapter 3 the book explains the rules for creating operations.

The difference between commands and operations is that operations output a value and commands do not. The Logo command output causes a procedure to stop running and output a value. The output command requires one input. It is a command and not an operation, so it does not output any value. (In other languages, the equivalent of output often does return some indication of whether it has succeeded in outputting a value.)

? help "output
OUTPUT value
OP value

        command.  Ends the running of the procedure in which it appears.
        That procedure outputs the value "value" to the context in which
        it was invoked.  Don't be confused: OUTPUT itself is a command,
        but the procedure that invokes OUTPUT is an operation.
The procedure outtest below accepts one input. It prints a message and then outputs the input it received.
to outtest :something
 print sentence [This procedure will now output the input value:] :something
 output :something
end
Remember that outtest is an operation. When you run it it produces an output, and Logo needs to know what to do with that output. Try running outtest in different ways to see what happens:
? outtest 79
? print outtest 79
? outtest [The quick brown fox]
? print outtest [The quick brown fox]
? show outtest [The quick brown fox]
We can modify outtest to confirm that it stops running after the output command is executed:
to outtest :something
 print sentence [This procedure will now output the input value:] :something
 output :something
 print [Procedure outtest is still running.]
end
Procedure outtest is a very simple operation but is not very useful. Page 48 of the textbook defines a procedure that outputs the second item in a list. The operation below is a procedure that outputs the vertical distance traveled in free-fall after a certain number of seconds.
to distance_fallen :time
 output 4.9 * :time * :time
end 
Page 49 of the text asks you to write a procedure that turns statements into questions. Can you write a procedure query that responds as shown?
? print query [I should have known better]
should I have known better?
? print query [you are experienced]
are you experienced?
If this seems too difficult, you may instead write a procedure that accepts two inputs -- the lengths of the sides of a right triangle -- and outputs the length of the hypotenuse of the triangle.

If these exercises are too easy for you, or if you finish early, attempt to write the procedure conj described at the bottom of page 46.