![]() |
Ch1 Ch2 Ch3 Ch4 Ch5 |
![]() |
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:
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:
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:
|
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:
![]() |
Runs the program, updating the world without tracing the code. |
![]() |
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. |
![]() |
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. |
![]() |
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. |
![]() |
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:
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
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:
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 | |
![]() |
![]() |
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:
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.
|
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 |
![]() |