Karel the Robot Learns Python

Ch1   Ch2   Ch3   Ch4   Ch5

Chapter 2
Defining Functions

One of the most powerful features of any programming language—including Karel—is the ability to define new functions. Whenever you have a sequence of Karel operations that performs some useful task—such as turning right—you can give that sequence a name. The operation of encapsulating a sequence of instructions under a new name is called defining a function.

The syntactic pattern for defining a function looks like this:

def name(): statements that make up the body of the function

The first word in this pattern is def, which is used in both Karel and Python to define a function. Words like def that have a specific, predefined meaning in a programming language are called keywords.

To define the turn_right function you needed in the last chapter, all you need to do is fill in the name and the list of steps, like this:

def turn_right(): turn_left() turn_left() turn_left()

Once you have defined a function like turn_right, you can think of it as a new built-in function, just like move or turn_left. In a sense, defining a function is like buying an upgrade for your robot that includes the missing operations.

Functions also make it possible to store a sequence of statements that solves a specific problem in Karel’s world. Collections of functions that implement a complete task are called programs. For example, the Karel program at the right of Figure 2-1 solves the move-beeper-to-ledge problem presented at the end of Chapter 1:

Figure 2-1. A Karel program that moves a beeper to the ledge

Running demo programs

Like the examples in Chapter 1, Figure 2-1 supports animation, making it possible for you to step through a program and watch it run. The icons under the world display have the following effects:

Run button Runs the program, updating the world without tracing the code.
Step button Executes one step of the program. If that step is a call to a user-defined function, the application steps into that function, so that you can see the entire history of the program operation.
Run button This button also executes a program step within the context of the current function. If that step is a call to a user-defined function, the application executes it as a single operation, stopping only when that function returns.
Speedometer The speedometer icon allows you to control the speed at which the application runs. Moving the speedometer needle clockwise increases the speed; moving it counterclockwise makes it run more slowly.
Reset button Resets the program and the world to their original state.

It is useful to step through this program using each of the two single-step commands to see how they differ. Everything will look exactly the same until you get to the turn_right statement. If you click on the first of these buttons, the instruction trace will move down to the code for turn_right and let you step through that function. If you instead click on the second of the step buttons, the instruction trace executes the turn_right function and then continues stepping at this level.

Comments

In addition to the definitions of move_beeper_to_ledge and turn_right, Figure 2-1 offers an illustration of an important programming feature called a comment, which consists of text designed to explain the operation of the program to human readers.

In Python—and the Python implementation of Karel—comments come in two styles. The first style is illustrated by the first line in the program listing, which looks like this:

# File: MoveBeeperToLedge.k

Comments of this form begin with a crosshatch character and extend up to the end of the line. In deference to web culture, I’ll call this style a hashtag comment. Here, the purpose of the hashtag comment is simply to indicate the name of the file containing the code.

The second commenting style is illustrated by the first line in each of the two functions. For example, the first line in the definition of turn_right is

"""Turns Karel right 90 degrees."""

which explains to anyone using the turn_right function what it does. This style of comment is called a docstring and consists of arbitrary text marked on each end with three consecutive quotation marks. Docstring comments often span multiple lines.

In a program this short, extensive comments are probably unnecessary. As programs become more complicated, however, comments quickly become essential tools to document the program design and make it easier for other programmers to understand.

Figure 2-1 also illustrates the technique of syntax coloring, which is the practice of using different colors in a program listing to make it easy to recognize different components of the program. The program listings on the web and in the course reader use cyan for hashtag comments, green to indicate strings (including docstrings), orange to identify language keywords like def, and black for the rest of the program text.

Using libraries

Although the program in Figure 2-1 explicitly includes the definition of the turn_right function, it is tedious to have to copy that code into every program that needs that function. For the most common operations, it makes sense to store them in a way that makes it easy to reuse them in other programs.

In computer science, collections of useful functions—and often other programming resources that you will learn about later—are called libraries. For example, the turn_right function and the equally useful turn_around function are both included in a Karel library called turns, which you can use simply by including the following line at the beginning of your program:

import turns

This statement asks Karel to use the turns library, which includes these two definitions. The Karel examples in the rest of this guide use the turns library to make the code easier to read.

Decomposition

Whenever you begin to solve a programming problem—no matter whether that program is written in Karel, Python, or any other programming language—one of your first tasks is to figure out how to divide the complete problem into smaller pieces called subproblems, each of which can be implemented as a separate function. That process is called decomposition. Decomposition is one of the most powerful strategies that programmers use to manage complexity and is probably the most important concept you need to learn from Karel.

To get a sense of how decomposition works in the context of a very simple problem, imagine that Karel is standing on a “road” as shown on the left side of the following before-and-after diagram:

before     after
World with two potholes     World with two potholes

Karel’s job is to fill each of the two potholes—the one on 2nd Avenue and the one on 5th Avenue—with a beeper and then continue on to the next corner, ending up in the position shown on the right.

Although you could solve this problem using the four predefined instructions, you can use functions to improve the structure of your program.  If nothing else, you can use turn_right and turn_around to shorten the program and make its intent clearer. More importantly, you can use decomposition to break the problem down into subproblems and then solve those problems independently. You can, for example, divide the problem of filling the pothole into the following subproblems:

  1. Move one block forward to reach the first pothole on 2nd Avenue.
  2. Fill the pothole by dropping a beeper into it.
  3. Move three blocks forward to reach the second pothole on 5th Avenue.
  4. Fill the pothole by dropping a beeper into it.
  5. Move one block forward to reach the desired final position.

If you think about the problem in this way, you can use functions to ensure that the program reflects your conception of the problem structure, as shown in Figure 2-2, which is animated so you can watch it work.

Figure 2-2. A program that fills two potholes

As with any programming problem, there are other decomposition strategies you might have tried. Some strategies make the program easier to read, while others only make the meaning more opaque. As your programming problems become more complex, decomposition will turn out to be one of the most important aspects of the design process.

Choosing an effective decomposition is much more of an art than a science, although you will find that you get better with practice. Chapter 4 presents some general guidelines that will help you in that process.

Ch1   Ch2   Ch3   Ch4   Ch5