Jump to content

Archive:Think Python/Case study: interface design: Difference between revisions

From IdeaWazaWiki
wikademia>Whiteknight
m Think Python: Automatically uploading HTML source of this book from http://www.greenteapress.com/thinkpython/html/. Will convert to wikitext in a separate step
 
wikademia>Wikademia
 
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
{{Think Python/Page}}
            "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD>


<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
== Chapter&#XA0;4&#XA0;&#XA0;Case study: interface design ==
<META name="GENERATOR" content="hevea 1.10">
 
<LINK rel="stylesheet" type="text/css" href="book.css">
 
<TITLE>Case study: interface design</TITLE>
 
</HEAD>
=== 4.1&#XA0;&#XA0;TurtleWorld ===
<BODY >
 
<A HREF="book004.html"><IMG SRC="previous_motif.gif" ALT="Previous"></A>
 
<A HREF="index.html"><IMG SRC="contents_motif.gif" ALT="Up"></A>
 
<A HREF="book006.html"><IMG SRC="next_motif.gif" ALT="Next"></A>
 
<HR>
 
<H1 CLASS="chapter"><A NAME="htoc44"><FONT COLOR=black><FONT SIZE=3>Chapter&#XA0;4</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Case study: interface design</FONT></FONT></H1><P><FONT COLOR=black><FONT SIZE=3>
To accompany this book, I have written a suite of modules called
</FONT></FONT><A NAME="turtlechap"></A></P><H2 CLASS="section"><A NAME="toc39"></A><A NAME="htoc45"><FONT COLOR=black><FONT SIZE=3>4.1</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;TurtleWorld</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>
</FONT></FONT><A NAME="@default290"></A><FONT COLOR=black><FONT SIZE=3>
</FONT></FONT><A NAME="@default291"></A></P><P><FONT COLOR=black><FONT SIZE=3>To accompany this book, I have written a suite of modules called
Swampy. One of these modules is TurtleWorld, which provides
Swampy. One of these modules is TurtleWorld, which provides
a set of functions for drawing lines by steering
a set of functions for drawing lines by steering
turtles around the screen.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>You can download Swampy from </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>thinkpython.com/swampy</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>;
turtles around the screen.
follow the instructions there to install Swampy on your system.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Move into the directory that contains </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>TurtleWorld.py</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>,
 
create a file named </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon.py</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and type in the following
You can download Swampy from <TT>thinkpython.com/swampy</TT>;
code:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>from TurtleWorld import *
follow the instructions there to install Swampy on your system.
 
Move into the directory that contains <TT>TurtleWorld.py</TT>,
create a file named <TT>polygon.py</TT> and type in the following
code:
<PRE CLASS="verbatim">from TurtleWorld import *


world = TurtleWorld()
world = TurtleWorld()
Line 30: Line 29:


wait_for_user()
wait_for_user()
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>The first line is a variation of the </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>import</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statement we saw before;
</PRE>
The first line is a variation of the <TT>import</TT> statement we saw before;
instead of creating a module object, it imports the functions
instead of creating a module object, it imports the functions
from the module directly, so you can access them without using dot
from the module directly, so you can access them without using dot
notation.</FONT></FONT></P><P><A NAME="@default292"></A><FONT COLOR=black><FONT SIZE=3>
notation.
</FONT></FONT><A NAME="@default293"></A></P><P><FONT COLOR=black><FONT SIZE=3>The next lines create a TurtleWorld assigned to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>world</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and
 
a Turtle assigned to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. Printing </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> yields something
 
like:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>&lt;TurtleWorld.Turtle instance at 0xb7bfbf4c&gt;
 
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>This means that </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> refers to
 
an </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>instance</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> of a Turtle
The next lines create a TurtleWorld assigned to <TT>world</TT> and
as defined in module </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>TurtleWorld</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. In this context,
a Turtle assigned to <TT>bob</TT>. Printing <TT>bob</TT> yields something
like:
<PRE CLASS="verbatim">&lt;TurtleWorld.Turtle instance at 0xb7bfbf4c&gt;
</PRE>
This means that <TT>bob</TT> refers to
an '''instance''' of a Turtle
as defined in module <TT>TurtleWorld</TT>. In this context,
&#X201C;instance&#X201D; means a member of a set;
&#X201C;instance&#X201D; means a member of a set;
this Turtle is one of the set of possible Turtles.</FONT></FONT></P><P><A NAME="@default294"></A></P><P><CODE><FONT COLOR=black><FONT SIZE=3>wait_for_user</FONT></FONT></CODE><FONT COLOR=black><FONT SIZE=3> tells TurtleWorld to wait for the user
this Turtle is one of the set of possible Turtles.
 
<CODE>wait_for_user</CODE> tells TurtleWorld to wait for the user
to do something, although in this case there&#X2019;s not much for
to do something, although in this case there&#X2019;s not much for
the user to do except close the window.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>TurtleWorld provides several
the user to do except close the window.
turtle-steering functions: </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>fd</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bk</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> for
 
forward and backward, and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>rt</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> for left and
TurtleWorld provides several
turtle-steering functions: <TT>fd</TT> and <TT>bk</TT> for
forward and backward, and <TT>lt</TT> and <TT>rt</TT> for left and
right turns. Also, each Turtle is holding a pen, which is
right turns. Also, each Turtle is holding a pen, which is
either down or up; if the pen is down, the Turtle leaves
either down or up; if the pen is down, the Turtle leaves
a trail when it moves. The functions </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>pu</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>pd</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
a trail when it moves. The functions <TT>pu</TT> and <TT>pd</TT>
stand for &#X201C;pen up&#X201D; and &#X201C;pen down.&#X201D;</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>To draw a right angle, add these lines to the program
stand for &#X201C;pen up&#X201D; and &#X201C;pen down.&#X201D;
(after creating </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and before calling </FONT></FONT><CODE><FONT COLOR=black><FONT SIZE=3>wait_for_user</FONT></FONT></CODE><FONT COLOR=black><FONT SIZE=3>):</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>fd(bob, 100)
 
To draw a right angle, add these lines to the program
(after creating <TT>bob</TT> and before calling <CODE>wait_for_user</CODE>):
<PRE CLASS="verbatim">fd(bob, 100)
rt(bob)
rt(bob)
fd(bob, 100)
fd(bob, 100)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>The first line tells </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> to take 100 steps
</PRE>
forward. The second line tells him to turn right.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>When you run this program, you should see </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> move east and then
The first line tells <TT>bob</TT> to take 100 steps
south, leaving two line segments behind.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Now modify the program to draw a square. Don&#X2019;t turn the page until
forward. The second line tells him to turn right.
you&#X2019;ve got it working!</FONT></FONT></P><H2 CLASS="section"><A NAME="toc40"></A><A NAME="htoc46"><FONT COLOR=black><FONT SIZE=3>4.2</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Simple repetition</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>
 
</FONT></FONT><A NAME="repetition"></A><FONT COLOR=black><FONT SIZE=3>
When you run this program, you should see <TT>bob</TT> move east and then
</FONT></FONT><A NAME="@default295"></A></P><P><FONT COLOR=black><FONT SIZE=3>Chances are you wrote something like this (leaving out the code
south, leaving two line segments behind.
that creates TurtleWorld and waits for the user):</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=black><FONT SIZE=3>fd(bob, 100)
 
Now modify the program to draw a square. Don&#X2019;t turn the page until
you&#X2019;ve got it working!
=== 4.2&#XA0;&#XA0;Simple repetition ===
 
 
 
 
 
Chances are you wrote something like this (leaving out the code
that creates TurtleWorld and waits for the user):
<PRE CLASS="verbatim">fd(bob, 100)
lt(bob)
lt(bob)


Line 69: Line 93:


fd(bob, 100)
fd(bob, 100)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>We can do the same thing more concisely with a </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statement.
</PRE>
Add this example to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon.py</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and run it again:</FONT></FONT></P><P><A NAME="@default296"></A><FONT COLOR=black><FONT SIZE=3>
We can do the same thing more concisely with a <TT>for</TT> statement.
</FONT></FONT><A NAME="@default297"></A><FONT COLOR=black><FONT SIZE=3>
Add this example to <TT>polygon.py</TT> and run it again:
</FONT></FONT><A NAME="@default298"></A></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>for i in range(4):
 
 
 
 
<PRE CLASS="verbatim">for i in range(4):
     print 'Hello!'
     print 'Hello!'
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>You should see something like this:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>Hello!
</PRE>
You should see something like this:
<PRE CLASS="verbatim">Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
Hello!
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>This is the simplest use of the </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statement; we will see
</PRE>
This is the simplest use of the <TT>for</TT> statement; we will see
more later. But that should be enough to let you rewrite your
more later. But that should be enough to let you rewrite your
square-drawing program. Don&#X2019;t turn the page until you do.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Here is a </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statement that draws a square:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>for i in range(4):
square-drawing program. Don&#X2019;t turn the page until you do.
 
Here is a <TT>for</TT> statement that draws a square:
<PRE CLASS="verbatim">for i in range(4):
     fd(bob, 100)
     fd(bob, 100)
     lt(bob)
     lt(bob)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>The syntax of a </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statement is similar to a function
</PRE>
The syntax of a <TT>for</TT> statement is similar to a function
definition. It has a header that ends with a colon and an indented
definition. It has a header that ends with a colon and an indented
body. The body can contain any number of statements.</FONT></FONT></P><P><A NAME="@default299"></A></P><P><FONT COLOR=black><FONT SIZE=3>A </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statement is sometimes called a </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>loop</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> because
body. The body can contain any number of statements.
 
A <TT>for</TT> statement is sometimes called a '''loop''' because
the flow of execution runs through the body and then loops back
the flow of execution runs through the body and then loops back
to the top. In this case, it runs the body four times.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>This version is actually a little different from the previous
to the top. In this case, it runs the body four times.
 
This version is actually a little different from the previous
square-drawing code because it makes another left turn after
square-drawing code because it makes another left turn after
drawing the last side of the square. The extra turn takes a little
drawing the last side of the square. The extra turn takes a little
Line 93: Line 132:
every time through the loop. This version also has the effect
every time through the loop. This version also has the effect
of leaving the turtle back in the starting position, facing in
of leaving the turtle back in the starting position, facing in
the starting direction.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc41"></A><A NAME="htoc47"><FONT COLOR=black><FONT SIZE=3>4.3</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Exercises</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>The following is a series of exercises using TurtleWorld. They
the starting direction.
=== 4.3&#XA0;&#XA0;Exercises ===
 
The following is a series of exercises using TurtleWorld. They
are meant to be fun, but they have a point, too. While you are
are meant to be fun, but they have a point, too. While you are
working on them, think about what the point is.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>The following sections have solutions to the exercises, so
working on them, think about what the point is.
don&#X2019;t look until you have finished (or at least tried).</FONT></FONT></P><OL CLASS="enumerate" type=1><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Write a function called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> that takes a parameter
 
named </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>t</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, which is a turtle. It should use the turtle to draw
The following sections have solutions to the exercises, so
a square.</FONT></FONT><P><FONT COLOR=black><FONT SIZE=3>Write a function call that passes </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> as an argument to
don&#X2019;t look until you have finished (or at least tried).
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, and then run the program again.</FONT></FONT></P></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Add another parameter, named </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>length</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.
 
Modify the body so length of the sides is </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>length</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, and then
*Write a function called <TT>square</TT> that takes a parameter
named <TT>t</TT>, which is a turtle. It should use the turtle to draw
a square.
Write a function call that passes <TT>bob</TT> as an argument to
<TT>square</TT>, and then run the program again.
 
*Add another parameter, named <TT>length</TT>, to <TT>square</TT>.
Modify the body so length of the sides is <TT>length</TT>, and then
modify the function call to provide a second argument. Run the
modify the function call to provide a second argument. Run the
program again. Test your program with a range of values for </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>length</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.</FONT></FONT></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>The functions </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>rt</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> make 90-degree turns by
program again. Test your program with a range of values for <TT>length</TT>.
 
*The functions <TT>lt</TT> and <TT>rt</TT> make 90-degree turns by
default, but you can provide a second argument that specifies the
default, but you can provide a second argument that specifies the
number of degrees. For example, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt(bob, 45)</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> turns </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> 45
number of degrees. For example, <TT>lt(bob, 45)</TT> turns <TT>bob</TT> 45
degrees to the left.</FONT></FONT><P><FONT COLOR=black><FONT SIZE=3>Make a copy of </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and change the name to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. Add
degrees to the left.
another parameter named </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>n</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and modify the body so it draws an
Make a copy of <TT>square</TT> and change the name to <TT>polygon</TT>. Add
another parameter named <TT>n</TT> and modify the body so it draws an
n-sided regular polygon. Hint: The angles of an n-sided regular
n-sided regular polygon. Hint: The angles of an n-sided regular
polygon are </FONT></FONT><FONT COLOR=black><FONT SIZE=3>360.0 / <I>n</I></FONT></FONT><FONT COLOR=black><FONT SIZE=3> degrees.</FONT></FONT></P><P><A NAME="@default300"></A><FONT COLOR=black><FONT SIZE=3>
polygon are 360.0 / <I>n</I> degrees.
</FONT></FONT><A NAME="@default301"></A></P></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Write a function called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> that takes a turtle, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>t</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>,
 
and radius, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>r</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, as parameters and that draws an approximate circle
 
by invoking </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> with an appropriate length and number of
 
sides. Test your function with a range of values of </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>r</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.</FONT></FONT><P><A NAME="@default302"></A><FONT COLOR=black><FONT SIZE=3>
 
</FONT></FONT><A NAME="@default303"></A></P><P><FONT COLOR=black><FONT SIZE=3>Hint: figure out the circumference of the circle and make sure that
*Write a function called <TT>circle</TT> that takes a turtle, <TT>t</TT>,
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>length * n = circumference</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Another hint: if </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is too slow for you, you can speed
and radius, <TT>r</TT>, as parameters and that draws an approximate circle
him up by changing </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob.delay</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, which is the time between moves,
by invoking <TT>polygon</TT> with an appropriate length and number of
in seconds. </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob.delay = 0.01</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> ought to get him moving.</FONT></FONT></P></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Make a more general version of </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
sides. Test your function with a range of values of <TT>r</TT>.
that takes an additional parameter </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>angle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, which determines
 
what fraction of a circle to draw. </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>angle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is in units of
 
degrees, so when </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>angle=360</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> should draw a complete
 
circle.</FONT></FONT><P><A NAME="@default304"></A><FONT COLOR=black><FONT SIZE=3>
Hint: figure out the circumference of the circle and make sure that
</FONT></FONT><A NAME="@default305"></A></P></LI></OL><H2 CLASS="section"><A NAME="toc42"></A><A NAME="htoc48"><FONT COLOR=black><FONT SIZE=3>4.4</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Encapsulation</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>The first exercise asks you to put your square-drawing code
<TT>length * n = circumference</TT>.
 
Another hint: if <TT>bob</TT> is too slow for you, you can speed
him up by changing <TT>bob.delay</TT>, which is the time between moves,
in seconds. <TT>bob.delay = 0.01</TT> ought to get him moving.
 
*Make a more general version of <TT>circle</TT> called <TT>arc</TT>
that takes an additional parameter <TT>angle</TT>, which determines
what fraction of a circle to draw. <TT>angle</TT> is in units of
degrees, so when <TT>angle=360</TT>, <TT>arc</TT> should draw a complete
circle.
 
 
 
=== 4.4&#XA0;&#XA0;Encapsulation ===
 
The first exercise asks you to put your square-drawing code
into a function definition and then call the function, passing
into a function definition and then call the function, passing
the turtle as a parameter. Here is a solution:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def square(t):
the turtle as a parameter. Here is a solution:
<PRE CLASS="verbatim">def square(t):
     for i in range(4):
     for i in range(4):
         fd(t, 100)
         fd(t, 100)
Line 129: Line 198:


square(bob)
square(bob)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>The innermost statements, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>fd</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> are
</PRE>
indented twice to show that they are inside the </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> loop,
The innermost statements, <TT>fd</TT> and <TT>lt</TT> are
indented twice to show that they are inside the <TT>for</TT> loop,
which is inside the function definition. The next line,
which is inside the function definition. The next line,
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square(bob)</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, is flush with the left margin, so that is the
<TT>square(bob)</TT>, is flush with the left margin, so that is the
end of both the </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>for</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> loop and the function definition.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Inside the function, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>t</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> refers to the same turtle </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
end of both the <TT>for</TT> loop and the function definition.
refers to, so </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt(t)</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> has the same effect as </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt(bob)</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.
 
So why not call the parameter </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>? The idea is that </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>t</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
Inside the function, <TT>t</TT> refers to the same turtle <TT>bob</TT>
can be any turtle, not just </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>bob</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, so you could create
refers to, so <TT>lt(t)</TT> has the same effect as <TT>lt(bob)</TT>.
a second turtle and pass it as an argument to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>ray = Turtle()
So why not call the parameter <TT>bob</TT>? The idea is that <TT>t</TT>
can be any turtle, not just <TT>bob</TT>, so you could create
a second turtle and pass it as an argument to <TT>square</TT>:
<PRE CLASS="verbatim">ray = Turtle()
square(ray)
square(ray)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>Wrapping a piece of code up in a function is called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>encapsulation</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. One of the benefits of encapsulation is that it
</PRE>
Wrapping a piece of code up in a function is called '''encapsulation'''. One of the benefits of encapsulation is that it
attaches a name to the code, which serves as a kind of documentation.
attaches a name to the code, which serves as a kind of documentation.
Another advantage is that if you re-use the code, it is more concise
Another advantage is that if you re-use the code, it is more concise
to call a function twice than to copy and paste the body!</FONT></FONT></P><P><A NAME="@default306"></A></P><H2 CLASS="section"><A NAME="toc43"></A><A NAME="htoc49"><FONT COLOR=black><FONT SIZE=3>4.5</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Generalization</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>The next step is to add a </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>length</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> parameter to </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>square</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.
to call a function twice than to copy and paste the body!
Here is a solution:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def square(t, length):
 
=== 4.5&#XA0;&#XA0;Generalization ===
 
The next step is to add a <TT>length</TT> parameter to <TT>square</TT>.
Here is a solution:
<PRE CLASS="verbatim">def square(t, length):
     for i in range(4):
     for i in range(4):
         fd(t, length)
         fd(t, length)
Line 149: Line 228:


square(bob, 100)
square(bob, 100)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>Adding a parameter to a function is called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>generalization</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
</PRE>
Adding a parameter to a function is called '''generalization'''
because it makes the function more general: in the previous
because it makes the function more general: in the previous
version, the square is always the same size; in this version
version, the square is always the same size; in this version
it can be any size.</FONT></FONT></P><P><A NAME="@default307"></A></P><P><FONT COLOR=black><FONT SIZE=3>The next step is also a generalization. Instead of drawing
it can be any size.
squares, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> draws regular polygons with any number of
 
sides. Here is a solution:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def polygon(t, n, length):
The next step is also a generalization. Instead of drawing
squares, <TT>polygon</TT> draws regular polygons with any number of
sides. Here is a solution:
<PRE CLASS="verbatim">def polygon(t, n, length):
     angle = 360.0 / n
     angle = 360.0 / n
     for i in range(n):
     for i in range(n):
Line 161: Line 244:


polygon(bob, 7, 70)
polygon(bob, 7, 70)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>This draws a 7-sided polygon with side length 70. If you have
</PRE>
This draws a 7-sided polygon with side length 70. If you have
more than a few numeric arguments, it is easy to forget what they
more than a few numeric arguments, it is easy to forget what they
are, or what order they should be in. It is legal, and sometimes
are, or what order they should be in. It is legal, and sometimes
helpful, to include the names of the parameters in the argument
helpful, to include the names of the parameters in the argument
list:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>polygon(bob, n=7, length=70)
list:
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>These are called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>keyword arguments</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> because they include
<PRE CLASS="verbatim">polygon(bob, n=7, length=70)
</PRE>
These are called '''keyword arguments''' because they include
the parameter names as &#X201C;keywords&#X201D; (not to be confused with
the parameter names as &#X201C;keywords&#X201D; (not to be confused with
Python keywords like </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>while</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>def</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>).</FONT></FONT></P><P><A NAME="@default308"></A><FONT COLOR=black><FONT SIZE=3>
Python keywords like <TT>while</TT> and <TT>def</TT>).
</FONT></FONT><A NAME="@default309"></A></P><P><FONT COLOR=black><FONT SIZE=3>This syntax makes the program more readable. It is also a reminder
 
 
 
 
This syntax makes the program more readable. It is also a reminder
about how arguments and parameters work: when you call a function, the
about how arguments and parameters work: when you call a function, the
arguments are assigned to the parameters.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc44"></A><A NAME="htoc50"><FONT COLOR=black><FONT SIZE=3>4.6</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Interface design</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>The next step is to write </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, which takes a radius,
arguments are assigned to the parameters.
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>r</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, as a parameter. Here is a simple solution that uses
=== 4.6&#XA0;&#XA0;Interface design ===
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> to draw a 50-sided polygon:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def circle(t, r):
 
The next step is to write <TT>circle</TT>, which takes a radius,
<TT>r</TT>, as a parameter. Here is a simple solution that uses
<TT>polygon</TT> to draw a 50-sided polygon:
<PRE CLASS="verbatim">def circle(t, r):
     circumference = 2 * math.pi * r
     circumference = 2 * math.pi * r
     n = 50
     n = 50
     length = circumference / n
     length = circumference / n
     polygon(t, n, length)
     polygon(t, n, length)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>The first line computes the circumference of a circle with radius
</PRE>
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>r</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> using the formula </FONT></FONT><FONT COLOR=black><FONT SIZE=3>2 &#X3C0; <I>r</I></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. Since we use </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>math.pi</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, we
The first line computes the circumference of a circle with radius
have to import </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>math</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. By convention, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>import</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> statements
<TT>r</TT> using the formula 2 &#X3C0; <I>r</I>. Since we use <TT>math.pi</TT>, we
are usually at the beginning of the script.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3><TT>n</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is the number of line segments in our approximation of a circle,
have to import <TT>math</TT>. By convention, <TT>import</TT> statements
so </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>length</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is the length of each segment. Thus, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
are usually at the beginning of the script.
draws a 50-sides polygon that approximates a circle with radius </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>r</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>One limitation of this solution is that </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>n</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is a constant, which
 
<TT>n</TT> is the number of line segments in our approximation of a circle,
so <TT>length</TT> is the length of each segment. Thus, <TT>polygon</TT>
draws a 50-sides polygon that approximates a circle with radius <TT>r</TT>.
 
One limitation of this solution is that <TT>n</TT> is a constant, which
means that for very big circles, the line segments are too long, and
means that for very big circles, the line segments are too long, and
for small circles, we waste time drawing very small segments. One
for small circles, we waste time drawing very small segments. One
solution would be to generalize the function by taking </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>n</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> as
solution would be to generalize the function by taking <TT>n</TT> as
a parameter. This would give the user (whoever calls </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>)
a parameter. This would give the user (whoever calls <TT>circle</TT>)
more control, but the interface would be less clean.</FONT></FONT></P><P><A NAME="@default310"></A></P><P><FONT COLOR=black><FONT SIZE=3>The </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>interface</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> of a function is a summary of how it is used: what
more control, but the interface would be less clean.
 
The '''interface''' of a function is a summary of how it is used: what
are the parameters? What does the function do? And what is the return
are the parameters? What does the function do? And what is the return
value? An interface is &#X201C;clean&#X201D; if it is &#X201C;as simple as
value? An interface is &#X201C;clean&#X201D; if it is &#X201C;as simple as
possible, but not simpler. (Einstein)&#X201D;</FONT></FONT></P><P><A NAME="@default311"></A></P><P><FONT COLOR=black><FONT SIZE=3>In this example, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>r</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> belongs in the interface because it
possible, but not simpler. (Einstein)&#X201D;
specifies the circle to be drawn. </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>n</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is less appropriate
 
because it pertains to the details of </FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>how</EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3> the circle should
In this example, <TT>r</TT> belongs in the interface because it
be rendered.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Rather than clutter up the interface, it is better
specifies the circle to be drawn. <TT>n</TT> is less appropriate
to choose an appropriate value of </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>n</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
because it pertains to the details of ''how'' the circle should
depending on </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circumference</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def circle(t, r):
be rendered.
 
Rather than clutter up the interface, it is better
to choose an appropriate value of <TT>n</TT>
depending on <TT>circumference</TT>:
<PRE CLASS="verbatim">def circle(t, r):
     circumference = 2 * math.pi * r
     circumference = 2 * math.pi * r
     n = int(circumference / 3) + 1
     n = int(circumference / 3) + 1
     length = circumference / n
     length = circumference / n
     polygon(t, n, length)
     polygon(t, n, length)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>Now the number of segments is (approximately) </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circumference/3</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>,
</PRE>
Now the number of segments is (approximately) <TT>circumference/3</TT>,
so the length of each segment is (approximately) 3, which is small
so the length of each segment is (approximately) 3, which is small
enough that the circles look good, but big enough to be efficient,
enough that the circles look good, but big enough to be efficient,
and appropriate for any size circle.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc45"></A><A NAME="htoc51"><FONT COLOR=black><FONT SIZE=3>4.7</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Refactoring</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>
and appropriate for any size circle.
</FONT></FONT><A NAME="refactoring"></A><FONT COLOR=black><FONT SIZE=3>
=== 4.7&#XA0;&#XA0;Refactoring ===
</FONT></FONT><A NAME="@default312"></A></P><P><FONT COLOR=black><FONT SIZE=3>When I wrote </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, I was able to re-use </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
 
 
 
 
 
When I wrote <TT>circle</TT>, I was able to re-use <TT>polygon</TT>
because a many-sided polygon is a good approximation of a circle.
because a many-sided polygon is a good approximation of a circle.
But </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is not as cooperative; we can&#X2019;t use </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
But <TT>arc</TT> is not as cooperative; we can&#X2019;t use <TT>polygon</TT>
or </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> to draw an arc.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>One alternative is to start with a copy
or <TT>circle</TT> to draw an arc.
of </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and transform it into </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. The result
 
might look like this:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def arc(t, r, angle):
One alternative is to start with a copy
of <TT>polygon</TT> and transform it into <TT>arc</TT>. The result
might look like this:
<PRE CLASS="verbatim">def arc(t, r, angle):
     arc_length = 2 * math.pi * r * angle / 360
     arc_length = 2 * math.pi * r * angle / 360
     n = int(arc_length / 3) + 1
     n = int(arc_length / 3) + 1
Line 220: Line 335:
         fd(t, step_length)
         fd(t, step_length)
         lt(t, step_angle)
         lt(t, step_angle)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>The second half of this function looks like </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, but we
</PRE>
can&#X2019;t re-use </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> without changing the interface. We could
The second half of this function looks like <TT>polygon</TT>, but we
generalize </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> to take an angle as a third argument,
can&#X2019;t re-use <TT>polygon</TT> without changing the interface. We could
but then </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> would no longer be an appropriate name!
generalize <TT>polygon</TT> to take an angle as a third argument,
Instead, let&#X2019;s call the more general function </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polyline</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def polyline(t, n, length, angle):
but then <TT>polygon</TT> would no longer be an appropriate name!
Instead, let&#X2019;s call the more general function <TT>polyline</TT>:
<PRE CLASS="verbatim">def polyline(t, n, length, angle):
     for i in range(n):
     for i in range(n):
         fd(t, length)
         fd(t, length)
         lt(t, angle)
         lt(t, angle)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>Now we can rewrite </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> to use </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polyline</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def polygon(t, n, length):
</PRE>
Now we can rewrite <TT>polygon</TT> and <TT>arc</TT> to use <TT>polyline</TT>:
<PRE CLASS="verbatim">def polygon(t, n, length):
     angle = 360.0 / n
     angle = 360.0 / n
     polyline(t, n, length, angle)
     polyline(t, n, length, angle)
Line 238: Line 357:
     step_angle = float(angle) / n
     step_angle = float(angle) / n
     polyline(t, n, step_length, step_angle)
     polyline(t, n, step_length, step_angle)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>Finally, we can rewrite </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>circle</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> to use </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def circle(t, r):
</PRE>
Finally, we can rewrite <TT>circle</TT> to use <TT>arc</TT>:
<PRE CLASS="verbatim">def circle(t, r):
     arc(t, r, 360)
     arc(t, r, 360)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>This process&#X2014;rearranging a program to improve function
</PRE>
interfaces and facilitate code re-use&#X2014;is called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>refactoring</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.
This process&#X2014;rearranging a program to improve function
In this case, we noticed that there was similar code in </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>arc</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> and
interfaces and facilitate code re-use&#X2014;is called '''refactoring'''.
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polygon</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>, so we &#X201C;factored it out&#X201D; into </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polyline</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>.</FONT></FONT></P><P><A NAME="@default313"></A></P><P><FONT COLOR=black><FONT SIZE=3>If we had planned ahead, we might have written </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polyline</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> first
In this case, we noticed that there was similar code in <TT>arc</TT> and
<TT>polygon</TT>, so we &#X201C;factored it out&#X201D; into <TT>polyline</TT>.
 
If we had planned ahead, we might have written <TT>polyline</TT> first
and avoided refactoring, but often you don&#X2019;t know enough at the
and avoided refactoring, but often you don&#X2019;t know enough at the
beginning of a project to design all the interfaces. Once you start
beginning of a project to design all the interfaces. Once you start
coding, you understand the problem better. Sometimes refactoring is a
coding, you understand the problem better. Sometimes refactoring is a
sign that you have learned something.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc46"></A><A NAME="htoc52"><FONT COLOR=black><FONT SIZE=3>4.8</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;A development plan</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>
sign that you have learned something.
</FONT></FONT><A NAME="@default314"></A></P><P><FONT COLOR=black><FONT SIZE=3>A </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>development plan</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is a process for writing programs.
=== 4.8&#XA0;&#XA0;A development plan ===
 
 
 
 
A '''development plan''' is a process for writing programs.
The process we used
The process we used
in this case study is &#X201C;encapsulation and
in this case study is &#X201C;encapsulation and
generalization.&#X201D; The steps of this process are:</FONT></FONT></P><OL CLASS="enumerate" type=1><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Start by writing a small program with no function definitions.</FONT></FONT></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Once you get the program working, encapsulate it in a function
generalization.&#X201D; The steps of this process are:
and give it a name.</FONT></FONT></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Generalize the function by adding appropriate parameters.</FONT></FONT></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Repeat steps 1&#X2013;3 until you have a set of working functions.
 
Copy and paste working code to avoid retyping (and re-debugging).</FONT></FONT></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3>Look for opportunities to improve the program by refactoring.
*Start by writing a small program with no function definitions.
 
*Once you get the program working, encapsulate it in a function
and give it a name.
 
*Generalize the function by adding appropriate parameters.
 
*Repeat steps 1&#X2013;3 until you have a set of working functions.
Copy and paste working code to avoid retyping (and re-debugging).
 
*Look for opportunities to improve the program by refactoring.
For example, if you have similar code in several places, consider
For example, if you have similar code in several places, consider
factoring it into an appropriately general function.</FONT></FONT></LI></OL><P><FONT COLOR=black><FONT SIZE=3>This process has some drawbacks&#X2014;we will see alternatives later&#X2014;but
factoring it into an appropriately general function.
 
This process has some drawbacks&#X2014;we will see alternatives later&#X2014;but
it can be useful if you don&#X2019;t know ahead of time how to divide the
it can be useful if you don&#X2019;t know ahead of time how to divide the
program into functions. This approach lets you design as you go
program into functions. This approach lets you design as you go
along.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc47"></A><A NAME="htoc53"><FONT COLOR=black><FONT SIZE=3>4.9</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;docstring</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>
along.
</FONT></FONT><A NAME="docstring"></A><FONT COLOR=black><FONT SIZE=3>
=== 4.9&#XA0;&#XA0;docstring ===
</FONT></FONT><A NAME="@default315"></A></P><P><FONT COLOR=black><FONT SIZE=3>A </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>docstring</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> is a string at the beginning of a function that
 
 
 
 
 
A '''docstring''' is a string at the beginning of a function that
explains the interface (&#X201C;doc&#X201D; is short for &#X201C;documentation&#X201D;). Here
explains the interface (&#X201C;doc&#X201D; is short for &#X201C;documentation&#X201D;). Here
is an example:</FONT></FONT></P><PRE CLASS="verbatim"><FONT COLOR=blue><FONT SIZE=4>def polyline(t, length, n, angle):
is an example:
<PRE CLASS="verbatim">def polyline(t, length, n, angle):
     """Draw n line segments with the given length and
     """Draw n line segments with the given length and
     angle (in degrees) between them.  t is a turtle.
     angle (in degrees) between them.  t is a turtle.
Line 269: Line 416:
         fd(t, length)
         fd(t, length)
         lt(t, angle)
         lt(t, angle)
</FONT></FONT></PRE><P><FONT COLOR=black><FONT SIZE=3>This docstring is a triple-quoted string, also known
</PRE>
This docstring is a triple-quoted string, also known
as a multiline string because the triple quotes allow the string
as a multiline string because the triple quotes allow the string
to span more than one line.</FONT></FONT></P><P><A NAME="@default316"></A><FONT COLOR=black><FONT SIZE=3>
to span more than one line.
</FONT></FONT><A NAME="@default317"></A><FONT COLOR=black><FONT SIZE=3>
 
</FONT></FONT><A NAME="@default318"></A><FONT COLOR=black><FONT SIZE=3>
 
</FONT></FONT><A NAME="@default319"></A><FONT COLOR=black><FONT SIZE=3>
 
</FONT></FONT><A NAME="@default320"></A></P><P><FONT COLOR=black><FONT SIZE=3>It is terse, but it contains the essential information
 
 
 
 
It is terse, but it contains the essential information
someone would need to use this function. It explains concisely what
someone would need to use this function. It explains concisely what
the function does (without getting into the details of how it does
the function does (without getting into the details of how it does
it). It explains what effect each parameter has on the behavior of
it). It explains what effect each parameter has on the behavior of
the function and what type each parameter should be (if it is not
the function and what type each parameter should be (if it is not
obvious).</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>Writing this kind of documentation is an important part of interface
obvious).
 
Writing this kind of documentation is an important part of interface
design. A well-designed interface should be simple to explain;
design. A well-designed interface should be simple to explain;
if you are having a hard time explaining one of your functions,
if you are having a hard time explaining one of your functions,
that might be a sign that the interface could be improved.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc48"></A><A NAME="htoc54"><FONT COLOR=black><FONT SIZE=3>4.10</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Debugging</FONT></FONT></H2><P><FONT COLOR=black><FONT SIZE=3>
that might be a sign that the interface could be improved.
</FONT></FONT><A NAME="@default321"></A><FONT COLOR=black><FONT SIZE=3>
=== 4.10&#XA0;&#XA0;Debugging ===
</FONT></FONT><A NAME="@default322"></A></P><P><FONT COLOR=black><FONT SIZE=3>An interface is like a contract between a function and a caller.
 
 
 
 
 
An interface is like a contract between a function and a caller.
The caller agrees to provide certain parameters and the function
The caller agrees to provide certain parameters and the function
agrees to do certain work.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>For example, </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>polyline</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> requires four arguments. The first
agrees to do certain work.
has to be a Turtle (or some other object that works with </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>fd</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>
 
and </FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>lt</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3>). The second has to be a number, and it should
For example, <TT>polyline</TT> requires four arguments. The first
has to be a Turtle (or some other object that works with <TT>fd</TT>
and <TT>lt</TT>). The second has to be a number, and it should
probably be positive, although it turns out that the function
probably be positive, although it turns out that the function
works even if it isn&#X2019;t. The third argument should be an integer;
works even if it isn&#X2019;t. The third argument should be an integer;
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><TT>range</TT></FONT></FONT><FONT COLOR=black><FONT SIZE=3> complains otherwise (depending on which version
<TT>range</TT> complains otherwise (depending on which version
of Python you are running). The fourth has to be a number,
of Python you are running). The fourth has to be a number,
which is understood to be in degrees.</FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3>These requirements are called </FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>preconditions</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3> because they
which is understood to be in degrees.
 
These requirements are called '''preconditions''' because they
are supposed to be true before the function starts executing.
are supposed to be true before the function starts executing.
Conversely, conditions at the end of the function are
Conversely, conditions at the end of the function are
</FONT></FONT><FONT COLOR=black><FONT SIZE=3><B>postconditions</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>. Postconditions include the intended
'''postconditions'''. Postconditions include the intended
effect of the function (like drawing line segments) and any
effect of the function (like drawing line segments) and any
side effects (like moving the Turtle or making other changes
side effects (like moving the Turtle or making other changes
in the World).</FONT></FONT></P><P><A NAME="@default323"></A><FONT COLOR=black><FONT SIZE=3>
in the World).
</FONT></FONT><A NAME="@default324"></A></P><P><FONT COLOR=black><FONT SIZE=3>Preconditions are the responsibility of the caller. If the caller
 
 
 
 
Preconditions are the responsibility of the caller. If the caller
violates a (properly documented!) precondition and the function
violates a (properly documented!) precondition and the function
doesn&#X2019;t work correctly, the bug is in the caller, not the function.
doesn&#X2019;t work correctly, the bug is in the caller, not the function.
Line 307: Line 474:
functions to check their preconditions rather than assume they are
functions to check their preconditions rather than assume they are
true. If every function checks its preconditions before starting,
true. If every function checks its preconditions before starting,
then if something goes wrong, you will know which function to blame.</FONT></FONT></P><H2 CLASS="section"><A NAME="toc49"></A><A NAME="htoc55"><FONT COLOR=black><FONT SIZE=3>4.11</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Glossary</FONT></FONT></H2><DL CLASS="description"><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>instance:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A member of a set. The TurtleWorld in this
then if something goes wrong, you will know which function to blame.
=== 4.11&#XA0;&#XA0;Glossary ===
 
<DL CLASS="description"><DT CLASS="dt-description">'''instance:'''</DT><DD CLASS="dd-description"> A member of a set. The TurtleWorld in this
chapter is a member of the set of TurtleWorlds.
chapter is a member of the set of TurtleWorlds.
</FONT></FONT><A NAME="@default325"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>loop:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A part of a program that can execute repeatedly.
</DD><DT CLASS="dt-description">'''loop:'''</DT><DD CLASS="dd-description"> A part of a program that can execute repeatedly.
</FONT></FONT><A NAME="@default326"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>encapsulation:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> The process of transforming a sequence of
</DD><DT CLASS="dt-description">'''encapsulation:'''</DT><DD CLASS="dd-description"> The process of transforming a sequence of
statements into a function definition.
statements into a function definition.
</FONT></FONT><A NAME="@default327"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>generalization:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> The process of replacing something
</DD><DT CLASS="dt-description">'''generalization:'''</DT><DD CLASS="dd-description"> The process of replacing something
unnecessarily specific (like a number) with something appropriately
unnecessarily specific (like a number) with something appropriately
general (like a variable or parameter).
general (like a variable or parameter).
</FONT></FONT><A NAME="@default328"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>keyword argument:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> An argument that includes the name of
</DD><DT CLASS="dt-description">'''keyword argument:'''</DT><DD CLASS="dd-description"> An argument that includes the name of
the parameter as a &#X201C;keyword.&#X201D;
the parameter as a &#X201C;keyword.&#X201D;
</FONT></FONT><A NAME="@default329"></A><FONT COLOR=black><FONT SIZE=3>
 
</FONT></FONT><A NAME="@default330"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>interface:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A description of how to use a function, including
</DD><DT CLASS="dt-description">'''interface:'''</DT><DD CLASS="dd-description"> A description of how to use a function, including
the name and descriptions of the arguments and return value.
the name and descriptions of the arguments and return value.
</FONT></FONT><A NAME="@default331"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>development plan:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A process for writing programs.
</DD><DT CLASS="dt-description">'''development plan:'''</DT><DD CLASS="dd-description"> A process for writing programs.
</FONT></FONT><A NAME="@default332"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>docstring:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A string that appears in a function definition
</DD><DT CLASS="dt-description">'''docstring:'''</DT><DD CLASS="dd-description"> A string that appears in a function definition
to document the function&#X2019;s interface.
to document the function&#X2019;s interface.
</FONT></FONT><A NAME="@default333"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>precondition:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A requirement that should be satisfied by
</DD><DT CLASS="dt-description">'''precondition:'''</DT><DD CLASS="dd-description"> A requirement that should be satisfied by
the caller before a function starts.
the caller before a function starts.
</FONT></FONT><A NAME="@default334"></A></DD><DT CLASS="dt-description"><FONT COLOR=black><FONT SIZE=3><B>postcondition:</B></FONT></FONT></DT><DD CLASS="dd-description"><FONT COLOR=black><FONT SIZE=3> A requirement that should be satisfied by
</DD><DT CLASS="dt-description">'''postcondition:'''</DT><DD CLASS="dd-description"> A requirement that should be satisfied by
the function before it ends.
the function before it ends.
</FONT></FONT><A NAME="@default335"></A></DD></DL><H2 CLASS="section"><A NAME="toc50"></A><A NAME="htoc56"><FONT COLOR=black><FONT SIZE=3>4.12</FONT></FONT></A><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;Exercises</FONT></FONT></H2><DIV CLASS="theorem"><FONT COLOR=black><FONT SIZE=3><B>Exercise&#XA0;1</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;</FONT></FONT><P><FONT COLOR=black><FONT SIZE=3><EM>Download the code in this chapter from
</DD></DL>=== 4.12&#XA0;&#XA0;Exercises ===
</EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>thinkpython.com/code/polygon.py</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>.</EM></FONT></FONT></P><OL CLASS="enumerate" type=1><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3><EM>Write appropriate docstrings for </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>polygon</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>, </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>arc</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM> and
 
</EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>circle</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>.</EM></FONT></FONT><P><A NAME="@default336"></A></P></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3><EM>Draw a stack diagram that shows the state of the program
<DIV CLASS="theorem">'''Exercise&#XA0;1'''&#XA0;&#XA0;
while executing </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>circle(bob, radius)</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>. You can do the
''Download the code in this chapter from
arithmetic by hand or add </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>print</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM> statements to the code.</EM></FONT></FONT></LI><LI CLASS="li-enumerate"><FONT COLOR=black><FONT SIZE=3><EM>The version of </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>arc</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM> in Section&#XA0;</EM></FONT></FONT><A HREF="#refactoring"><FONT COLOR=black><FONT SIZE=3><EM>4.7</EM></FONT></FONT></A><FONT COLOR=black><FONT SIZE=3><EM> is not
''''<TT>thinkpython.com/code/polygon.py</TT>''''.''
 
*''Write appropriate docstrings for ''''<TT>polygon</TT>'''', ''''<TT>arc</TT>'''' and
''''<TT>circle</TT>''''.''
 
*''Draw a stack diagram that shows the state of the program
while executing ''''<TT>circle(bob, radius)</TT>''''. You can do the
arithmetic by hand or add ''''<TT>print</TT>'''' statements to the code.''
 
*''The version of ''''<TT>arc</TT>'''' in Section&#XA0;''''4.7'''' is not
very accurate because the linear approximation of the
very accurate because the linear approximation of the
circle is always outside the true circle. As a result,
circle is always outside the true circle. As a result,
Line 337: Line 516:
destination. My solution shows a way to reduce
destination. My solution shows a way to reduce
the effect of this error. Read the code and see if it makes
the effect of this error. Read the code and see if it makes
sense to you. If you draw a diagram, you might see how it works.</EM></FONT></FONT></LI></OL></DIV><DIV CLASS="theorem"><FONT COLOR=black><FONT SIZE=3><B>Exercise&#XA0;2</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;<EM>
sense to you. If you draw a diagram, you might see how it works.''
</EM></FONT></FONT><A NAME="@default337"></A><P><FONT COLOR=black><FONT SIZE=3><EM>Write an appropriately general set of functions that
 
can draw flowers like this:</EM></FONT></FONT></P><DIV CLASS="center"><FONT COLOR=black><FONT SIZE=3><EM><IMG SRC="book005.png"></EM></FONT></FONT></DIV><P><FONT COLOR=black><FONT SIZE=3><EM>You can download a solution from </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>thinkpython.com/code/flower.py</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>.</EM></FONT></FONT></P></DIV><DIV CLASS="theorem"><FONT COLOR=black><FONT SIZE=3><B>Exercise&#XA0;3</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;<EM>
</DIV><DIV CLASS="theorem">'''Exercise&#XA0;2'''&#XA0;&#XA0;''
</EM></FONT></FONT><A NAME="@default338"></A><P><FONT COLOR=black><FONT SIZE=3><EM>Write an appropriately general set of functions that
''
can draw shapes like this:</EM></FONT></FONT></P><DIV CLASS="center"><FONT COLOR=black><FONT SIZE=3><EM><IMG SRC="book006.png"></EM></FONT></FONT></DIV><P><FONT COLOR=black><FONT SIZE=3><EM>You can download a solution from </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>thinkpython.com/code/pie.py</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>.</EM></FONT></FONT></P></DIV><DIV CLASS="theorem"><FONT COLOR=black><FONT SIZE=3><B>Exercise&#XA0;4</B></FONT></FONT><FONT COLOR=black><FONT SIZE=3>&#XA0;&#XA0;<EM>
''Write an appropriately general set of functions that
</EM></FONT></FONT><A NAME="@default339"></A><FONT COLOR=black><FONT SIZE=3><EM>
can draw flowers like this:''
</EM></FONT></FONT><A NAME="@default340"></A><FONT COLOR=black><FONT SIZE=3><EM>
<DIV CLASS="center">''<IMG SRC="book005.png">''</DIV>
</EM></FONT></FONT><A NAME="@default341"></A><P><FONT COLOR=black><FONT SIZE=3><EM>The letters of the alphabet can be constructed from a moderate
''You can download a solution from ''''<TT>thinkpython.com/code/flower.py</TT>''''.''
</DIV><DIV CLASS="theorem">'''Exercise&#XA0;3'''&#XA0;&#XA0;''
''
''Write an appropriately general set of functions that
can draw shapes like this:''
<DIV CLASS="center">''<IMG SRC="book006.png">''</DIV>
''You can download a solution from ''''<TT>thinkpython.com/code/pie.py</TT>''''.''
</DIV><DIV CLASS="theorem">'''Exercise&#XA0;4'''&#XA0;&#XA0;''
''''
''''
''
''The letters of the alphabet can be constructed from a moderate
number of basic elements, like vertical and horizontal lines
number of basic elements, like vertical and horizontal lines
and a few curves. Design a font that can be drawn with a
and a few curves. Design a font that can be drawn with a
minimal number of basic elements and then write functions
minimal number of basic elements and then write functions
that draw letters of the alphabet.</EM></FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3><EM>You should write one function for each letter, with names
that draw letters of the alphabet.''
</EM></FONT></FONT><CODE><FONT COLOR=black><FONT SIZE=3><EM>draw_a</EM></FONT></FONT></CODE><FONT COLOR=black><FONT SIZE=3><EM>, </EM></FONT></FONT><CODE><FONT COLOR=black><FONT SIZE=3><EM>draw_b</EM></FONT></FONT></CODE><FONT COLOR=black><FONT SIZE=3><EM>, etc., and put your functions
 
in a file named </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>letters.py</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>. You can download a
''You should write one function for each letter, with names
&#X201C;turtle typewriter&#X201D; from </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>thinkpython.com/code/typewriter.py</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>
''<CODE>''draw_a''</CODE>'', ''<CODE>''draw_b''</CODE>'', etc., and put your functions
to help you test your code.</EM></FONT></FONT></P><P><FONT COLOR=black><FONT SIZE=3><EM>You can download a solution from </EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM><TT>thinkpython.com/code/letters.py</TT></EM></FONT></FONT><FONT COLOR=black><FONT SIZE=3><EM>.</EM></FONT></FONT></P></DIV><HR>
in a file named ''''<TT>letters.py</TT>''''. You can download a
<A HREF="book004.html"><IMG SRC="previous_motif.gif" ALT="Previous"></A>
&#X201C;turtle typewriter&#X201D; from ''''<TT>thinkpython.com/code/typewriter.py</TT>''''
<A HREF="index.html"><IMG SRC="contents_motif.gif" ALT="Up"></A>
to help you test your code.''
<A HREF="book006.html"><IMG SRC="next_motif.gif" ALT="Next"></A>
 
</BODY>
''You can download a solution from ''''<TT>thinkpython.com/code/letters.py</TT>''''.''
</HTML>
</DIV><HR>
<IMG SRC="previous_motif.gif" ALT="Previous">
<IMG SRC="contents_motif.gif" ALT="Up">
<IMG SRC="next_motif.gif" ALT="Next">

Latest revision as of 20:09, 18 May 2009

Chapter 4  Case study: interface design

4.1  TurtleWorld

To accompany this book, I have written a suite of modules called Swampy. One of these modules is TurtleWorld, which provides a set of functions for drawing lines by steering turtles around the screen.

You can download Swampy from thinkpython.com/swampy; follow the instructions there to install Swampy on your system.

Move into the directory that contains TurtleWorld.py, create a file named polygon.py and type in the following code:

from TurtleWorld import *

world = TurtleWorld()
bob = Turtle()
print bob

wait_for_user()

The first line is a variation of the import statement we saw before; instead of creating a module object, it imports the functions from the module directly, so you can access them without using dot notation.



The next lines create a TurtleWorld assigned to world and a Turtle assigned to bob. Printing bob yields something like:

<TurtleWorld.Turtle instance at 0xb7bfbf4c>

This means that bob refers to an instance of a Turtle as defined in module TurtleWorld. In this context, “instance” means a member of a set; this Turtle is one of the set of possible Turtles.

wait_for_user tells TurtleWorld to wait for the user to do something, although in this case there’s not much for the user to do except close the window.

TurtleWorld provides several turtle-steering functions: fd and bk for forward and backward, and lt and rt for left and right turns. Also, each Turtle is holding a pen, which is either down or up; if the pen is down, the Turtle leaves a trail when it moves. The functions pu and pd stand for “pen up” and “pen down.”

To draw a right angle, add these lines to the program (after creating bob and before calling wait_for_user):

fd(bob, 100)
rt(bob)
fd(bob, 100)

The first line tells bob to take 100 steps forward. The second line tells him to turn right.

When you run this program, you should see bob move east and then south, leaving two line segments behind.

Now modify the program to draw a square. Don’t turn the page until you’ve got it working!

4.2  Simple repetition

Chances are you wrote something like this (leaving out the code that creates TurtleWorld and waits for the user):

fd(bob, 100)
lt(bob)

fd(bob, 100)
lt(bob)

fd(bob, 100)
lt(bob)

fd(bob, 100)

We can do the same thing more concisely with a for statement. Add this example to polygon.py and run it again:



for i in range(4):
    print 'Hello!'

You should see something like this:

Hello!
Hello!
Hello!
Hello!

This is the simplest use of the for statement; we will see more later. But that should be enough to let you rewrite your square-drawing program. Don’t turn the page until you do.

Here is a for statement that draws a square:

for i in range(4):
    fd(bob, 100)
    lt(bob)

The syntax of a for statement is similar to a function definition. It has a header that ends with a colon and an indented body. The body can contain any number of statements.

A for statement is sometimes called a loop because the flow of execution runs through the body and then loops back to the top. In this case, it runs the body four times.

This version is actually a little different from the previous square-drawing code because it makes another left turn after drawing the last side of the square. The extra turn takes a little more time, but it simplifies the code if we do the same thing every time through the loop. This version also has the effect of leaving the turtle back in the starting position, facing in the starting direction.

4.3  Exercises

The following is a series of exercises using TurtleWorld. They are meant to be fun, but they have a point, too. While you are working on them, think about what the point is.

The following sections have solutions to the exercises, so don’t look until you have finished (or at least tried).

  • Write a function called square that takes a parameter

named t, which is a turtle. It should use the turtle to draw a square. Write a function call that passes bob as an argument to square, and then run the program again.

  • Add another parameter, named length, to square.

Modify the body so length of the sides is length, and then modify the function call to provide a second argument. Run the program again. Test your program with a range of values for length.

  • The functions lt and rt make 90-degree turns by

default, but you can provide a second argument that specifies the number of degrees. For example, lt(bob, 45) turns bob 45 degrees to the left. Make a copy of square and change the name to polygon. Add another parameter named n and modify the body so it draws an n-sided regular polygon. Hint: The angles of an n-sided regular polygon are 360.0 / n degrees.



  • Write a function called circle that takes a turtle, t,

and radius, r, as parameters and that draws an approximate circle by invoking polygon with an appropriate length and number of sides. Test your function with a range of values of r.


Hint: figure out the circumference of the circle and make sure that length * n = circumference.

Another hint: if bob is too slow for you, you can speed him up by changing bob.delay, which is the time between moves, in seconds. bob.delay = 0.01 ought to get him moving.

  • Make a more general version of circle called arc

that takes an additional parameter angle, which determines what fraction of a circle to draw. angle is in units of degrees, so when angle=360, arc should draw a complete circle.


4.4  Encapsulation

The first exercise asks you to put your square-drawing code into a function definition and then call the function, passing the turtle as a parameter. Here is a solution:

def square(t):
    for i in range(4):
        fd(t, 100)
        lt(t)

square(bob)

The innermost statements, fd and lt are indented twice to show that they are inside the for loop, which is inside the function definition. The next line, square(bob), is flush with the left margin, so that is the end of both the for loop and the function definition.

Inside the function, t refers to the same turtle bob refers to, so lt(t) has the same effect as lt(bob). So why not call the parameter bob? The idea is that t can be any turtle, not just bob, so you could create a second turtle and pass it as an argument to square:

ray = Turtle()
square(ray)

Wrapping a piece of code up in a function is called encapsulation. One of the benefits of encapsulation is that it attaches a name to the code, which serves as a kind of documentation. Another advantage is that if you re-use the code, it is more concise to call a function twice than to copy and paste the body!

4.5  Generalization

The next step is to add a length parameter to square. Here is a solution:

def square(t, length):
    for i in range(4):
        fd(t, length)
        lt(t)

square(bob, 100)

Adding a parameter to a function is called generalization because it makes the function more general: in the previous version, the square is always the same size; in this version it can be any size.

The next step is also a generalization. Instead of drawing squares, polygon draws regular polygons with any number of sides. Here is a solution:

def polygon(t, n, length):
    angle = 360.0 / n
    for i in range(n):
        fd(t, length)
        lt(t, angle)

polygon(bob, 7, 70)

This draws a 7-sided polygon with side length 70. If you have more than a few numeric arguments, it is easy to forget what they are, or what order they should be in. It is legal, and sometimes helpful, to include the names of the parameters in the argument list:

polygon(bob, n=7, length=70)

These are called keyword arguments because they include the parameter names as “keywords” (not to be confused with Python keywords like while and def).



This syntax makes the program more readable. It is also a reminder about how arguments and parameters work: when you call a function, the arguments are assigned to the parameters.

4.6  Interface design

The next step is to write circle, which takes a radius, r, as a parameter. Here is a simple solution that uses polygon to draw a 50-sided polygon:

def circle(t, r):
    circumference = 2 * math.pi * r
    n = 50
    length = circumference / n
    polygon(t, n, length)

The first line computes the circumference of a circle with radius r using the formula 2 π r. Since we use math.pi, we have to import math. By convention, import statements are usually at the beginning of the script.

n is the number of line segments in our approximation of a circle, so length is the length of each segment. Thus, polygon draws a 50-sides polygon that approximates a circle with radius r.

One limitation of this solution is that n is a constant, which means that for very big circles, the line segments are too long, and for small circles, we waste time drawing very small segments. One solution would be to generalize the function by taking n as a parameter. This would give the user (whoever calls circle) more control, but the interface would be less clean.

The interface of a function is a summary of how it is used: what are the parameters? What does the function do? And what is the return value? An interface is “clean” if it is “as simple as possible, but not simpler. (Einstein)”

In this example, r belongs in the interface because it specifies the circle to be drawn. n is less appropriate because it pertains to the details of how the circle should be rendered.

Rather than clutter up the interface, it is better to choose an appropriate value of n depending on circumference:

def circle(t, r):
    circumference = 2 * math.pi * r
    n = int(circumference / 3) + 1
    length = circumference / n
    polygon(t, n, length)

Now the number of segments is (approximately) circumference/3, so the length of each segment is (approximately) 3, which is small enough that the circles look good, but big enough to be efficient, and appropriate for any size circle.

4.7  Refactoring

When I wrote circle, I was able to re-use polygon because a many-sided polygon is a good approximation of a circle. But arc is not as cooperative; we can’t use polygon or circle to draw an arc.

One alternative is to start with a copy of polygon and transform it into arc. The result might look like this:

def arc(t, r, angle):
    arc_length = 2 * math.pi * r * angle / 360
    n = int(arc_length / 3) + 1
    step_length = arc_length / n
    step_angle = float(angle) / n
    
    for i in range(n):
        fd(t, step_length)
        lt(t, step_angle)

The second half of this function looks like polygon, but we can’t re-use polygon without changing the interface. We could generalize polygon to take an angle as a third argument, but then polygon would no longer be an appropriate name! Instead, let’s call the more general function polyline:

def polyline(t, n, length, angle):
    for i in range(n):
        fd(t, length)
        lt(t, angle)

Now we can rewrite polygon and arc to use polyline:

def polygon(t, n, length):
    angle = 360.0 / n
    polyline(t, n, length, angle)

def arc(t, r, angle):
    arc_length = 2 * math.pi * r * angle / 360
    n = int(arc_length / 3) + 1
    step_length = arc_length / n
    step_angle = float(angle) / n
    polyline(t, n, step_length, step_angle)

Finally, we can rewrite circle to use arc:

def circle(t, r):
    arc(t, r, 360)

This process—rearranging a program to improve function interfaces and facilitate code re-use—is called refactoring. In this case, we noticed that there was similar code in arc and polygon, so we “factored it out” into polyline.

If we had planned ahead, we might have written polyline first and avoided refactoring, but often you don’t know enough at the beginning of a project to design all the interfaces. Once you start coding, you understand the problem better. Sometimes refactoring is a sign that you have learned something.

4.8  A development plan

A development plan is a process for writing programs. The process we used in this case study is “encapsulation and generalization.” The steps of this process are:

  • Start by writing a small program with no function definitions.
  • Once you get the program working, encapsulate it in a function

and give it a name.

  • Generalize the function by adding appropriate parameters.
  • Repeat steps 1–3 until you have a set of working functions.

Copy and paste working code to avoid retyping (and re-debugging).

  • Look for opportunities to improve the program by refactoring.

For example, if you have similar code in several places, consider factoring it into an appropriately general function.

This process has some drawbacks—we will see alternatives later—but it can be useful if you don’t know ahead of time how to divide the program into functions. This approach lets you design as you go along.

4.9  docstring

A docstring is a string at the beginning of a function that explains the interface (“doc” is short for “documentation”). Here is an example:

def polyline(t, length, n, angle):
    """Draw n line segments with the given length and
    angle (in degrees) between them.  t is a turtle.
    """    
    for i in range(n):
        fd(t, length)
        lt(t, angle)

This docstring is a triple-quoted string, also known as a multiline string because the triple quotes allow the string to span more than one line.




It is terse, but it contains the essential information someone would need to use this function. It explains concisely what the function does (without getting into the details of how it does it). It explains what effect each parameter has on the behavior of the function and what type each parameter should be (if it is not obvious).

Writing this kind of documentation is an important part of interface design. A well-designed interface should be simple to explain; if you are having a hard time explaining one of your functions, that might be a sign that the interface could be improved.

4.10  Debugging

An interface is like a contract between a function and a caller. The caller agrees to provide certain parameters and the function agrees to do certain work.

For example, polyline requires four arguments. The first has to be a Turtle (or some other object that works with fd and lt). The second has to be a number, and it should probably be positive, although it turns out that the function works even if it isn’t. The third argument should be an integer; range complains otherwise (depending on which version of Python you are running). The fourth has to be a number, which is understood to be in degrees.

These requirements are called preconditions because they are supposed to be true before the function starts executing. Conversely, conditions at the end of the function are postconditions. Postconditions include the intended effect of the function (like drawing line segments) and any side effects (like moving the Turtle or making other changes in the World).



Preconditions are the responsibility of the caller. If the caller violates a (properly documented!) precondition and the function doesn’t work correctly, the bug is in the caller, not the function. However, for purposes of debugging it is often a good idea for functions to check their preconditions rather than assume they are true. If every function checks its preconditions before starting, then if something goes wrong, you will know which function to blame.

4.11  Glossary

instance:
A member of a set. The TurtleWorld in this chapter is a member of the set of TurtleWorlds.
loop:
A part of a program that can execute repeatedly.
encapsulation:
The process of transforming a sequence of statements into a function definition.
generalization:
The process of replacing something unnecessarily specific (like a number) with something appropriately general (like a variable or parameter).
keyword argument:
An argument that includes the name of the parameter as a “keyword.”
interface:
A description of how to use a function, including the name and descriptions of the arguments and return value.
development plan:
A process for writing programs.
docstring:
A string that appears in a function definition to document the function’s interface.
precondition:
A requirement that should be satisfied by the caller before a function starts.
postcondition:
A requirement that should be satisfied by the function before it ends.

=== 4.12  Exercises ===

Exercise 1  

Download the code in this chapter from 'thinkpython.com/code/polygon.py'.

  • Write appropriate docstrings for 'polygon', 'arc' and

'circle'.

  • Draw a stack diagram that shows the state of the program

while executing 'circle(bob, radius)'. You can do the arithmetic by hand or add 'print' statements to the code.

  • The version of 'arc' in Section '4.7' is not

very accurate because the linear approximation of the circle is always outside the true circle. As a result, the turtle ends up a few units away from the correct destination. My solution shows a way to reduce the effect of this error. Read the code and see if it makes sense to you. If you draw a diagram, you might see how it works.

Exercise 2  

Write an appropriately general set of functions that can draw flowers like this:

<IMG SRC="book005.png">

You can download a solution from 'thinkpython.com/code/flower.py'.

Exercise 3  

Write an appropriately general set of functions that can draw shapes like this:

<IMG SRC="book006.png">

You can download a solution from 'thinkpython.com/code/pie.py'.

Exercise 4  

' ' The letters of the alphabet can be constructed from a moderate number of basic elements, like vertical and horizontal lines and a few curves. Design a font that can be drawn with a minimal number of basic elements and then write functions that draw letters of the alphabet.

You should write one function for each letter, with names draw_a, draw_b, etc., and put your functions in a file named 'letters.py'. You can download a “turtle typewriter” from 'thinkpython.com/code/typewriter.py' to help you test your code.

You can download a solution from 'thinkpython.com/code/letters.py'.


<IMG SRC="previous_motif.gif" ALT="Previous"> <IMG SRC="contents_motif.gif" ALT="Up"> <IMG SRC="next_motif.gif" ALT="Next">