Background
jLogo Programming
- Commanding a Turtle
- Pseudocode
- Adding New Commands
- Iteration & Animation
- Hierarchical Structure
- Procedure Inputs
- Operators & Expressions
- Defining Operators
- Words & Sentences
- User Interface Events
- What If? (Predicates)
- Recursion
- Local Variables
- Global Variables
- Word/Sentence Iteration
- Mastermind Project
- Turtles As Actors
- Arrays
Java
- A Java Program
- What's a Class?
- Extending Existing Classes
- Types
- Turtle Graphics
- Control Flow
- User Interface Events
Appendices
Lastly
Defining Operators
Introduction
In the previous lesson you learned about operators, procedures that produce an output. Most of the operators you used took an input or inputs and used it/them in some way to produce the output.
In this lesson, you will learn
- how to output a value from a procedure that you write; you can write your own operators, another mechanism for creating more powerful procedural abstractions, and
- about symbolic constants (names for values that never change) and how a procedure that outputs a value can serve this purpose.
Symbolic Constants
As I described in the very first lesson, a computer only works with binary numbers, called bits. But I went on to show how things that you will work with (e.g., characters, decimal numbers) are built out of these bits. Systems programmers (wizards that write very low-level programs like assemblers, interpreters, compilers, system libraries, etc...) have been providing these niceties for almost ever. This allows you to write programs that you can read, that make sense to you.
The programs you are now writing are starting to get big and they are taking hours to write. In a few lessons, you will be writing programs that take many hours to complete. This means that the programming will be spread over days. Each time you return to continue programming, you need to pick back up where you left off. The point is that your programs need to be easy for you to read so that you can refresh your mind on what's done and what's left to do. I suggest that you get into the habit of giving names to many of the numbers that are starting to become ubiquitous in your programs.
The most obvious case is the numbers for the colors that you provide as an input to the setpencolor command. I have trouble remembering many of the color numbers. So, what I have done is to write little procedures that output numbers corresponding to the color the procedure's identifier represents.
This binding of a number to a meaningful identifier is what's called a symbolic constant in programming terms.
Here are a few examples:
|
Once defined, the first two of these procedures can be used as inputs to setpencolor. The colors above are some of the colors I used in my seascape program. Here is what part of my main procedure contained - the part that used the color procedures.
|
Defining the procedures blue and forest and then using them in appropriate places makes the program much easier to read. Trust me, this is good...
The symbolic constants for headings for the turtle are used similarly. north, east, etc... procedures can be used as inputs to the setheading command. Get into the habit of starting all of your programs with symbolic constants - part of the vocabulary for the story you are about to write. Yes, think of the process of writing a program as similar to writing anything that is descriptive.
Creating Your Own Operators
In the previous lesson there were examples of arithmetic expressions that computed and displayed the circumference and the area of a circle. Here is one of the procedures.
|
It would be more useful to separate the println functionality from the computation piece. It's simple... Just replace println with output.
|
Notice that I changed the name of the procedure. You always want the name of the procedure to reflect what it does. This is how we achieve our abstraction.
This new procedure, now an operator, is simple to use with the println command, e.g., here is an example of its use in the CommandCenter.
|
Use the following TG applet to try it out for yourself. Type the circleCircumference definition into the Editor, then invoke it a few times in the CommandCenter to see what you get.
TG - TurtleGraphics Applet
Practice: A Couple More Operators
You really don't deeply learn about something by reading about it or hearing someone lecture about it - you learn by trying to do it. Here are a few exercises that you can play with.
-
Write a procedure named circleArea with an input for the radius of the circle.
It should produce an output that is the area of the circle. Test it with
the inputs in Table 9.1 and make sure you get good answers.
Table 9.1Input Area 2 12.56636 3 28.27431 5 78.53975 11 380.13242 - Part of the arithmetic expression you wrote to compute the area of a circle involved squaring a number. Squaring a number is a common thing to do. Write a procedure named square with an input :number that produces number-squared. There is also a constant value in the expression, PI, which should be defined as a symbolic constant - define the procedure PI which outputs 3.14159. Redefine your circleArea using these new procedures. Once again, test it...
Project: Random In Range
OK, now for an operator that would have been nice to have in one of the programs we wrote in the last lesson, RandomBoxes. To refresh your memory, we drew a bunch of solid, colored boxes at random locations in the GraphicsCanvas. We needed a couple of random numbers in ranges other than the standard 0...x-1 provided by the random x operator. We needed random numbers in a range determined by the height and width of the GraphicsCanvas. As an example, if the canvaswidth operator outputs 600, we needed random numbers in the range of -300 ... 300-boxWidth.
I want a new operator, let's call it randomInRange, which produces an output, a number that's in any range of numbers I provide as inputs. As an example, for the invocation "randomInRange -50 50" I want an output that is random and is greater-than or equal-to -50 AND is less-than or equal-to 50. Figure 9.1 demonstrates a working version producing random numbers in the range of -2 through 2.
|
Ok, either go off and write it or if you need a little help to get started, here's a skeleton.
|
At this point, don't hesitate to play around in the TG applet to figure out what you need to do. Follow the steps we've been using to write all of our programs.
- Understanding the Problem (figure out what you know, and what you don't know)
- Devising a Plan (write a pseudo code description of what to do, draw a plumbing diagram or diagrams to visualize what you will do)
- Carrying out the Plan (type in your source code to do the job and test it; does it work? if not, verify your code matches what you developed in step 2 and review what you came up with in steps 1 and 2)
Don't read further until you've at least made some attempt at determining what you will do.
First hint...
If you think about it, your output needs to be at least what the input :min is given when randomInRange is invoked. Since random returns 0 at a minimum, you are going to need to add :min to the output from random. So, our pseudocode now looks like:
|
Don't read further until you've made an attempt to complete this procedure and played with it (invoke it in the CommandCenter).
Second hint...
At this point, I suggest that you write an operator named magnitude. Here's the expanded pseudo code:
|
OK... finish the procedure.
Use repeat to test your code. If it doesn't work, help is on the way - read the next section. If your code works, congratulations! Now read the next section because it's a really cool feature you are sure to use in the future.
TRACE - A Debugging Tool
"Failure is an integral part of success," Mead says. "You
learn from every one of your failures. I used to tell
students, 'You've got to listen to the silicon. It's trying
to tell you something.'"
If you build something or do something and it doesn't work
out, he says you can curse and swear at it. Or, you can learn
from it.
"The physical world is perfectly willing to share with you
how it works. If you listen. But, if you have your mind made
up, you can go for years and not hear it," Mead says.
from an article in Investors Business Daily
May 13, 2003
Time for you to learn about a feature in TG, tracing, that will help you understand what's happening when you're program is being executed. trace is a directive you enter into the CommandCenter window of TG. trace is similar to a command, but it can't be put into the body of a procedure. It directs TG to print out very useful stuff as your program is interpreted.
So once you have your magnitude and randomInRange procedures entered. In the TG CommandCenter window, type:
|
Since TG has been directed to trace the procedure magnitude, what you should see is somthing like:
|
TG let you know that magnitude was invoked with the inputs: -4 and 4. It's body was executed and its output was 8.
Well, with this information, you can see that there is a problem with the code I gave you. There's a bug in it. randomInRange -4 4 will never output a 4 (the value provided for the :max input in our trial).
random 8 outputs a number in the range 0 - 7. To get an output of 4 we need to give random an input equal to the output of magnitude plus 1.
Fix the bug (if you hadn't discovered it and fixed it on your own)...
Finally, modify your program from the last lesson that draws boxes at random locations so that it uses randomInRange. Also add symbolic constants where appropriate... Use them to make your program more readable.
I'm not saying that you are going to need an operator built into that we have not talked about, but I used it. Table 9.1 gives you the scoop on minus which simply takes a number and negates it, just like multiplying it by -1.
| Command | Inputs | Description |
| MINUS | number | Outputs the negative of number | |
Project: A Grid Toolkit
The reason that I have had you drawing axes and grids in many of the previous lessons is that they are very common in programs. Here are some windows which I captured; they are, top-to-bottom, left-to-right:
- Programming a Robot in Machine Language
- Mastermind Game
- Sudoku Puzzle Assistant
![]() |
|
|
|
- Game of Life
- Turtle Solving Maze
- Paint Program (Big Pixels)
|
|
![]() |
|
Each of these programs contains at least one graphical object that is grid-like. I am going to take our use of abstraction to another level. We are going to write a bunch of procedures that provide stuff we will need in many programs. We are going to get started on a Grid Toolkit.
A Grid Toolkit Contract
The first thing I'm going to do is write the rules for using it. These rules are going to be a contract. We will follow the rules when we write the procedures in the toolkit. And... we will follow the rules when we use the procedures in the toolkit in our programs. I talked a bit about contracts, as they applied to a single procedure, when I introduced defining you own procedures. Our first Grid Toolkit rules will be
- Definition: our grid is a bunch of squares, all of equal size, arranged in rows, each with an equal number of columns
- Definition: the squares making up our grid will be called cells
- All procedures in the toolkit will have names starting with "grid"
-
A cell can be identified in one of two ways:
-
by its row and column numbers. Rows are ordered from top to bottom,
with the top row numbered zero. Columns are ordered left to right
with the leftmost column numbered zero.
-
by its index. the index (number) of the top-left cell is zero and
then indicies increase left-to-right and then top-to-bottom.
-
by its row and column numbers. Rows are ordered from top to bottom,
with the top row numbered zero. Columns are ordered left to right
with the leftmost column numbered zero.
The Grid Toolkit Code
Given these rules, here are the procedures that make up our first pass at a Grid Toolkit.
|
Checkout the Grid Toolkit
Copy & Paste this into TG's Editor so you can play with it. It has initial values in place in all of the procedures acting as symbolic constants. These will be changed as necessary in programs that you write that include the GridToolkit. But, with my initial values in place, you can display a grid by invoking a single procedure: gridPaint. Then you can paint individual cells by invoking gridCellFill with two inputs, the cell's index and the color number it is to be filled with. In the CommandCenter, type:
|
Why the Gift?
So, do you know why I gave you all of the source code for the Grid Toolkit without making you work for it?
Answer: there is a lot to be learned by reading someone else's source code.
Long ago, when I was convinced that Logo was a much better language to to use to introduce programming concepts than Java, the first thing I did was to read a lot of Logo source code. Fortunately for me, there is a lot of it available. Brian Harvey's Computer Science, Logo Style books, available free on-line, are a great introduction. See the ItP acknowledgements page for additional sources of great Logo programs.
Now it's your turn to read some code. Read through the Grid Toolkit source code and answer the following questions.
Draw a Ball in a Cell
Write a procedure that draws a colored ball in the center of a specified cell. Use procedures that are in the Grid Toolkit to do most of the work. All you should write is the code to draw the ball.
Use of New Primitive Operator
In the Grid Toolkit, I used a primitive operator that I have not yet introduced you to. What is it? Why did I need to use it? Hint: temporarily remove it and see what happens when you do. Try invoking gridCellFill with different inputs after you have removed it.
Why Count Starting With Zero?
In Grid Toolkit, rows and columns were numbered starting with zero instead of one. The index that is used to identify a particular cell also started with the first cell numbered zero. This convention has been in place for decades in the world of computer programming.
The reason is that this simplifies the code. Can you find the code in our GridWorld which is simpler than it would have to be if we started numbering the index at one instead of zero? How would the code need to be changed if the base was one instead of zero?
Summary
We've made another quantum jump in how we can reduce the complexity of the programs we write. You now know how to define your own operators, procedures which output values. Symbolic constants, a very simple form of a user-defined operator, can make your programs much more readable. And, binding meaningful names to complex expressions, is just one more use of abstraction, an approach to writing programs that are much more easily understood. And, the side benefit is that a program that is easy to read, that's easily understood. is that it will probably do what you want, what you intended it to do.
Go to the Table of Contents
On to Words & Sentences

