BFOIT

Introduction to Programming

    Defining Your Own Commands    


*Note*    In 2007, these lessons were significantly modified.    *Note*
This is a link to the new materials.
This is a link to the new adding commands lesson.


Introduction

In this lesson you will learn how to teach the Logo interpreter in TG new commands.  When you startup TG, it only understands the commands built into its Logo interpreter, the basic Logo language primitive procedures.  You will teach the Logo interpreter how to do something it does not yet know how to do.

This capability of naming procedures which are made up of already existing instructions is very powerful.  As you are about to learn, it is a big help in constructing large programs.


Defining Your Own Commands - Procedural Abstraction

In the last lesson, you learned to break your programs into pieces, each piece easy to understand by itself.  Your program was the concatenation of the pieces.  This approach is very important for writing programs that do what you want them to do.

For review, here are our steps in the programming process:

  1. think about what you have to do and approaches to it,

  2. break up the program into pieces written in pseudocode, and

  3. convert the pseudocode to Logo instructions and type them in.

We are going to look at an alternative to using pseudocode.  There's a way to replace the notes and pseudocode with another way of representing what we are thinking.  We will use Logo commands we write and a process that's called "stepwise refinement" to create programs.

Writing our own Logo commands will get us procedural abstraction, an important separation of the description of what something does from how it does it.  For now, don't worry about this technical term.  Just think of it as jargon for inventing new commands that do one thing, something that is the first thing you think of when you hear the name you give to the command.

Procedural abstraction is the most important programming concept you will learn!

Let's work through an example.  It will give you a good feel for what I'm attempting to describe.

Pseudocode Logo Instructions
  draw a box  
  each side = 100 steps  
  fd 100 rt 90 fd 100 rt 90 fd 100 rt 90 fd 100 rt 90  
Table 4.1

By now you have to be good at drawing a square box.  Table 4.1 shows a pseudocode description of what to do and its comparable list of Logo instructions. 

Here are a few problems I have with the list of instructions.

  1. without the pseudocode, it takes a nontrivial amount of thinking to figure out what this list of instructions does,
  2. if it is in the middle of other instructions it is even harder to figure out what this combination of instructions does,
  3. it takes a lot of time to type every time you want a square box that's 100 steps on a side, and
  4. it consists of so many characters that it is easy to make a typing error.

What we need is the ability to give a name (also known as an identifier) to the list of instructions that draw this square box.  And, we need to make the Logo interpreter aware of this name and treat it just like the built-in (aka primitive) commands it already has. 

We can do this.  The Logo interpreter in TG allows you to create your own commands.  Here's how to define a new command that draws a box, 100 steps on a side.

   to box100
     fd 100 rt 90
     fd 100 rt 90
     fd 100 rt 90
     fd 100 rt 90
     end

Here is the TG applet; click the mouse in the CommandCenter and type it in 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!
TG Applet

You could have typed in:

   to box100
     fd 100 rt 90 fd 100 rt 90 fd 100 rt 90 fd 100 rt 90
     end
I prefer the first example because it is easier to read and with the symmetry of the four lines, it's easy to see that it is correct.

So, what have we done here?

We've used a special command ("to") to teach the Logo interpreter a new command.  Just as it understands "forward" and "right" commands, it now understands what a "box100" command is and does.  Since you defined it, it's called a user-defined procedure

Try out your new command.  In the CommandCenter, type "box100[Enter]" and see what this does.  Did it do what you expected? 

If not, here are a couple of diagnostic tools that will help you find out why not.

TG Directive Description Example
  printprocs  
  pp
  Print a list of user-defined procedures the Logo interpreter in TG knows.     ? printprocs  
  box100
  ?
  printtext  
  pt
  Print the text (the source code representation) of a specified user-defined procedure.     ? printtext box100  
  to box100
      fd 100 rt 90
      fd 100 rt 90
      fd 100 rt 90
      fd 100 rt 90
      end
  ?
Table 4.2

Even if your box100 procedure did work, you might want to check these handy tools out.  Click the mouse in the CommandCenter and enter the command:

    printprocs
The name of your new procedure, box100, should be printed. 

If it is, next try:

    printtext box100
"printtext box100" asks the Logo interpreter in TG to print out the definition of the box100 command.  Check it out to see what's wrong.  Reenter the definition until it matches what one of the above examples looks like and works as you would expect.

Once it's working, it's time to reflect on what we have just done.  The Logo interpreter now knows what to do when it gets a box100 command.  We introduced this command with a TO command.

What does this special "TO" command look like?

The general form, the syntax, of a "TO" command, a procedure definition is:

   to       <name>   
     <Logo-Instructions>   
   ...   
   end   
Figure 4.1

Procedure definitions consist of:

  1. a title line (the "to" line containing the procedure's name, its identifier),
  2. a body consisting of one or more lines of instructions, and
  3. an end line (the word "end")

Once you've defined box100 you can use it like any of the Logo commands you've been using.  You've just taught the Logo interpreter how to do something; you've added a new command to Logo's vocabulary.

Either go back up to the TG applet or use a small popup TG applet to play around with your new command.  Try using it to draw a couple of boxes at different locations in TurtleSpace.

Then, try typing:

   box100 rt 45
a few times...  What does this do?  cool, huh?

Define another command that draws a different size of box, or a triangle, or something else...  Have fun!  Explore!


Procedure Definition vs Procedure Invocation

When you typed in the definition of box100, did anything surprise you?

Did you notice that defining your own command (a procedure) causes the TurtleGraphics applet to behave differently?

Until now, every time you typed instructions into the CommandCenter, when you pressed the [Enter] key, something happened.  When you typed "fd 100 rt 90" into the CommandCenter, the turtle would move forward in the graphics area of the applet and then rotate 90 degrees to the right.  But, when you typed in your new command, which included four sets of "fd 100 rt 90" instruction lists, nothing happened in TurtleSpace.  Is was as if you were typing into TG's editor, not into the CommandCenter.

Go back up to the TG applet and define some new command, something as simple as fwd100 that does nothing more than a single "fd 100" instruction.  What you type is not important - just watch what happens as you type in the definition.

Did you notice that when you typed in: "to fwd100" and pressed [Enter], the CommandCenter name stripe changed:

"Defining Procedure: fwd100"
appeared to the left of the word "CommandCenter" and the cursor moved to the next line, positioned to the right of a different prompt.  The characters "> " are to the left of the cursor instead of the "? " characters you've been seeing.  Also, the cursor was automatically indented a couple of spaces. 

When you continued typing, adding an instruction line, e.g., "fd 100" and then pressed the "Enter" key, again, nothing happened in the graphics area.  But, the "> " characters remained the prompt and the cursor was still indented a couple of spaces on the next new line. 

Although it is not obvious, the Logo interpreter is doing something - it's checking to make sure that what you are typing is valid Logo.  If it is, what you are typing is simply collected, stored away someplace, remembered for later use.  You are teaching the interpreter how to do something new and it is remembering what you are typing.

Finally, when you typed in "end" and pressed the "Enter" key, (called the end line) the phrase "Defining&nbps;Procedure:&nbps;fwd100" disappeared in the CommandCenter's name stripe, the prompt characters went back to "? " and the cursor was no longer indented a couple of spaces. 

You had completed the definition of the new command.  All of the stuff that changed - the CommandCenter's name stripe, the prompt, the extra indentation - were feedback to you from the interpreter, to make sure you knew that you were in the process of defining a new command.

Once you had completed defining your new command, you could then use it.  The Logo interpreter now recognizes it and does what you told it to do when you defined it.  Typing its name to get the interpreter to perform it technically called invoking it, a.k.a. doing it, executing it, calling it, ...

In summary,

 - defining a procedure is teaching the interpreter how to do something, identified by a supplied name;

 - invoking a procedure (typing the procedure's name into the CommandCenter) is telling the interpreter to do what you taught it.


Logo Animation - Watching Procedure Invocation

Here is a small program consisting of two procedure definitions (main and box100) and one invocation of main and four invocations of box100.  Watch how the program is executed, step by step.

alt="Your browser understands the <APPLET> tag but isn't running the applet, for some reason." Your browser is completely ignoring the <APPLET> tag!


Using Procedural Abstraction to Rewrite DrawHouse

Now that you know how to define Logo procedures, I'm going to show you how you can use them to write programs more easily.  The programs you write will be much easier to read and to extend.  Let's take the instructions that you wrote which draw a house and turn them into a bunch of procedures.

I'll use TG's editor.  If you have access to the TG application, start it up now, otherwise click here to get a small popup TG applet.  Use one of these to follow along.

Use the Window->Editor->Open menu option to get TG's editor.  Drag the CommandCenter and Editor name stripes to grow the editor subwindow to a reasonable size.  To get maximum space for the editor, you can temporarily close the GraphicsCanvas with the Window->Canvas->Close menu option.

In the introduction, I mentioned the term "stepwise refinement" and this is how we will approach the task of writing a new DrawHouse program.  What this means is that we will breakdown the writing into a series of steps.  With each step, we will get closer to a complete program that does what we want - we refine our program.

All right, so the first thing I want to do is write a procedure which draws a house.  I go to the editor window, click the left mouse button in it, and type:

  to drawHouse
followed by [Enter].  The editor responds by opening a blank line and adding an end line following it.  The cursor is placed on the middle, blank, line and is indented; it's ready for instructions which are to be part of my drawHouse procedure.

So what now?  Where do we go from here?  How can we break down drawing the house into multiple, simpler tasks?  What are the objects that make up our house?  That's it!  I'll break the procedure of drawing the house into subprocedures, one for each object.  My drawHouse procedure will invoke four other new procedures:

  1. drawFront - a procedure that draws the front of the house
  2. drawRoof - a procedure that draws the roof on top of its front
  3. drawDoor - a procedure that draws the front door, and
  4. drawWindow - a procedure that draws the front window.

So, I add invocations to these procedures to my drawHouse procedure in the editor.  I make it look like this:

   to drawHouse
     drawFront
     drawRoof
     drawDoor
     drawWindow
     end
This is a great first step.  What I just did was to think about what I needed, planned how to break the problem up into a set of simpler problems, and then I wrote source code that should work.

But, this is only legal Logo source code IF I continue on and define all of the drawXxxx procedures I've invoked.  Figure 4.2. shows what happens if I request that the interpreter perform the program as it stands (via Interpret->Editor Contents menu item).  An error message pops up.

Figure 4.2

What I could do is turn the lines of source code that are not yet ready into comments.  When the interpreter sees the semicolon (";") indicating the start of a comment, it simply collects it and all the remaining characters on the line.  This text is assumed to be a note for the programmer and anyone else reading the source code.

So, for now, I'll change the procedure to:

  to drawHouse
    ;drawFront
    ;drawRoof
    ;drawDoor
    ;drawWindow
    end
And now it's time to write each of the needed procedures.  I position the cursor on the "to drawHouse" line, hold down the [Ctrl] key and press the "o" key.  This opens an empty line.  I am now ready to add drawFront.  As I did for drawHouse, I type in:
  to drawFront
followed by [Enter].  The editor responds by opening a blank line, followed by an end line, and positions the cursor, ready for me to type in the body.  I add commands which get the turtle to draw the front of a house (same code as box100).  Once I've done this, I can go back to the drawHouse procedure and remove the semicolon from the drawFront invocation.

I can now test what I've done so far.  I drag the name stripes on the CommandCenter and the Editor to minimize their height, click the mouse in the CommandCenter and type "drawHouse" which invokes this procedure.  The front of the house, a box, gets drawn.

By repeating this process with the remaining procedures I refine my program until it is completed.

Were you following along?  If not, go do what I did now.

Did you get a house?  If not check your source code and fix the mistakes.

Once you get a house, here's an exercise for your: instruct the turtle to pick up the pen, move to another spot, put the pen down.  Now try invoking your DrawHouse procedure again.  Did you get a second house?  If so, does it look like the first?  If not, figure out why.


The Procedure: main

There is one more procedure that you should write.  Its name is main.  Why?  What should it do? 

What a procedure named main should do is initialize the GraphicsCanvas (aka TurtleSpace).  One possibility is to make sure everything is like it is when you first see the TG applet.  This state is the same as when you startup the TG application.  This state can be described as:

Another possibility is to set some of the attributes to a state that you want for your program, e.g., maybe you want the pen size to start out at a width of 6 turtle-steps or the pen color to be blue. 

Writing programs in TG is highly interactive.  As you try things out, write procedures and invoke them, you modify TG's state.  You move the turtle around; you change its heading; you change the color of its pen; etc...  Testing parts of your program as you go is great; this helps you write a correct program fast.  But, a well written program has a known starting point and state and this is main.  It should contain all of the commands needed to intialize TG's state properly. 

Here's an example main procedure for use with drawHouse.

  to main
    home clean setheading 0
    pendown setpencolor 0 setpensize 2
    drawHouse
    end
Finally, what's with the name: main?  I've chosen this name because it is also the required starting procedure for programs written in the programming languages: C, C++, and Java.


Exercises

Write a program that draws a word.  Your program should have a procedure for each letter in the word, a procedure drawXxxx (where Xxxx is replaced with your word, and a main procedure.  As an example, I wrote a program which had the procedure structure as shown below and output (a drawing) also shown...


          main
            |
            |
            |
            |
            |
         drawGUY
         /  |  \
        /   |   \
       /    |    \
      /     |     \
     /      |      \
   drawG  drawU  drawY    


Summary

Think about how we've broken large programming problems into steps of manageable size.  You've learned how to package them into procedures that have names which describe what the procedure does.  This is called procedural abstraction.

This has been your first exposure to the use of abstraction - the most powerful concept you will use as a programmer.

A Piece of History: In the mid seventies, I stopped in to see a new friend in his office.  He had stepped out for a bit, so I decided to wait.  I checked out his bookshelf and grabbed a small, plain-black book that had the title "Structured Programming" written by O.J. Dahl, E.W. Dijkstra, and C.A.R. Hoare.  A quick glance at its Table of Contents and the title of one chapter written by E.W. Dijkstra caught my eye - "On Our Inability To Do Much."  I scanned the chapter and knew this book was monumental; I had to buy a copy and read it.  This book is still in print, more than thirty years after it was published.  What this lesson has attempted to introduce to you was at the heart of the matter in Dijkstra's contributions to this wonderful book.


Back to Table of Contents
Back to previous Lesson ( Pseudocode )
On to next Lesson ( Iteration )