BFOIT

Introduction to Programming

Defining Your Own Operators
and Hierarchy


Introduction

In the previous lesson you learned about operators, procedures that produce an output.  The primitive 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

  1. 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,
  2. about symbolic constants (names for values that never change) and how a procedure that outputs a value can serve in this purpose, and
  3. 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.


Symbolic Constants

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 specify 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 constant in 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 TurtleTalk 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 forest makes 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 TurtleTalk 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!
TurtleGraphics Applet


A couple of exercises:

  1. 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. 
  2. 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 TurtleTalk applet to figure out what you need to do.  Follow the steps we've been using to write all of our TurtleTalk 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 the TurtleTalk 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

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

Finally, modify your program from the last lesson that draws boxes at random locations so that it uses randomInRange.


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 7.2 show the hierarchy of the DrawStreet program.

Figure 7.2

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 DrawHouse are.  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.


Summary

In this lesson, you learned how to write an operator which 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 TurtleTalk 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 TurtleTalk Procedures Used In This Lesson
Name Input Description Example
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


Back to Table of Contents
Back to previous Lesson ( TurtleTalk Operators & Expressions )
On to next Lesson ( TurtleTalk Words and User Interface )