In the previous lesson you learned about operators, procedures that produce an output. The primitive operators you used took an input or inputsand 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 you own operators, another mechanism for creating more powerful procedural abstractions,
- about symbolic constants (names for values that never change) and how a procedure that outputs a value can serve this purpose, and
- the importance of the hierarchical organization of procedures It makes your programs easier to write so that they do what you want. Programs that you write in a clear, hierachical manner are also easier to read, modify, and extend.
As I described in the very first lesson, a computer only knows about binary numbers. 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. My 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.
Here are a few examples:
to blue output 1 end to forest output 10 end to salmon output 12 end
This binding of a number to a meaningful identifier is what’s called a symbolic constantin programming terms.
Once defined, you can use these procedures as the input to setpencolor. The colors above are the colors I used in my seascape program. Here is what part of my main procedure contains – the part that uses the color procedures.
to main hideturtle setpencolor blue waves -240 20 waves -130 -15 setpencolor forest fish 100 -100 fish 50 -150 ...
Using the procedures blue and forestmakes the program much easier to read.
Writing Your Own Operators
In the last 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.
to printCircleCircum :radius println product 2 product 3.14159 :radius end
It would be more useful to separate the println functionality from the computation piece. It’s simple… Just replace println with output.
to circleCircumference :radius output product 2 product 3.14159 :radius end
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.,
println circleCircumference 4
“25.13272” is displayed in the CommandCenter window.
Use the following TG applet to try it out for yourself.
|alt=”Your browser understands the <APPLET> tag but isn’t running the applet, for some reason.” Your browser is completely ignoring the <APPLET> tag!|
A couple of exercises:
- 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 above.
- 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. Redefine your circleArea using it. Test it…
OK, now for an operator that would have been nice to have in one of the programs we wrote in the last lesson. To refresh your memory, we needed a couple of random numbers in ranges other than the standard 0…x-1 provided by a “random x” operator. We needed random numbers in the range -285…235 for the X coordinate and in the range -225…175 for the Y coordinate.
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.
Here’s the skeleton.
to randomInRange :min :max ; output <random number gtr-or-eql :min and less-or-eql :max> end
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.
1. Understanding the Problem (figure out what you know, and what you don't know) 2. Devising a Plan (write a pseudo code description of what to do, draw a plumbing diagram or diagrams to visualize what you will do) 3. 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)
To get you started… If you think about it, your output needs to be at least what is provided for :min when randomInRange is invoked. Since random returns 0 at a minimum, you are going to have to add :min to the output from random in the output produced. So, our pseudo code now looks like:
to randomInRange :min :max ; output sum :min random <magnitude of range of numbers> end
At this point, I suggest that you write an operator named magnitude. Here’s the expanded pseudo code:
to magnitude :min :max ; output <magnitude of range of numbers> end to randomInRange :min :max output sum :min random magnitude :min :max end
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! &nbpsp;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. traceis 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 you have your magnitude and randomInRange procedures entered. In the TG CommandCenter window, type:
trace magnitude print randomInRange -4 4
Since TG has been directed to trace the procedure magnitude, what you should see is:
Entering magnitude -4 4 Exiting magnitude 8
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 4.
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)…
Hierarchical Structure – Why It’s Important
A hierarchy is a grouping of things into levels. There is a “top” level and then a series of lower levels under it. It’s all about abstraction. At each level you describe a concept with enough detail for you to have a good feel for what lies below it.
In the previous lesson you wrote a program that drew a series of houses, each similar (they all had a roof, door, and window) but different (their heights and widths varied). The elegant solution to this problem was to write procedures that drew the pieces of the house, e.g., the door, the window, etc… Each of these procedures had inputs that were the desired location and size of the house. Each procedure adjusted itself to fit this criteria. Each of these procedures was composed of a bunch of primitives, i.e., penup, pendown, setxy, forward, quotient, etc… You had to know all of these, what they did. But, the next level up in the hierarchy – your DrawHouse procedure – there were NO primitives needed. Your DrawHouse simply combined the procedures that you had written. Here is my version:
to DrawHouse :x :y :height :width DrawFront :x :y :height :width DrawRoof :x :y :height :width DrawDoor :x :y :height :width DrawWindow :x :y :height :width end
This shows the power of abstraction and hierarchy. With abstraction, a programmer does not need to know how a procedure does what it is documented to do – just that it does it. Hierarchy lets a programmer build more and more complicated structures, one on top of the other. Figure 8.2 show the hierarchy of the DrawStreetprogram.
At the highest level in the program’s hierarchy, all it had to know about was the procedure: DrawHouse. Given it, the street was drawn as such:
to DrawStreet DrawHouse -265 -100 100 150 DrawHouse -105 -100 120 100 DrawHouse 5 -100 200 120 DrawHouse 135 -100 70 110 end
And, the only thing that someone needs to know in order to modify this program to draw a different set of houses is what the inputs to DrawHouseare. There is no need to know even a single primitive procedure! This is very powerful and is the heart of programming.
Building things in hierarchies is very common in computer software. One example are the file systems provided by operating systems. File systems have a top-level, often referred to as the root of the file system. Under the root, there are subdirectories like “My Documents” and “Program Files” and under these are more subdirectories.
In this lesson, you learned how to write an operatorwhich is a procedure that produces an output. You now know how to write your own abstractions which can be used to produce values that provide an input to any procedure. You can write components that fit into higher levels of data flow through a plumbing diagram.
And, speaking about graphical representations of your programs, I ended the lesson with a sales-pitch to get you to write programs that have well-defined hierarchies.
|New jLogo Procedures Used In This Lesson|
|OUTPUT||value||Execution of the current procedure is complete. output‘s input (value) is passed back to the instruction that the current procedure’s invocation is part of.||OUTPUT 1|