TurtleWorld

  • Turtle graphics are a simple way of drawing on the screen
  • Imagine a turtle walking around carrying a pen
  • He can:
    • Pull the pen up
    • Push the pen down
    • Move forward
    • Turn to the left or right
  • That's it!

In [1]:
import turtle

# NOTE: Many statements in this notebook are commented out as turtle graphics can have
#       in a Jupyter Notebook.  You can use uncommented versions in your Python IDE.

#bob = turtle.Turtle()
#print( bob )

#bob.fd( 100 ) # Move forward 100 pixels
#bob.lt( 90 )  # Turn 90 degrees to the left
#bob.fd( 100 ) # Move forward 100 pixels

Simple repetition

  • When designing algorithms, we often need to repeat some action over and over
  • One way to implement this in Python is the for loop

In [2]:
for i in range(4):
    print( 'Hello!' )


Hello!
Hello!
Hello!
Hello!

To practice loops and turtle graphics, complete the following exercises from the textbook:

  1. 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.
  2. 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.
  3. 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 exterior angles of an n-sided regular polygon are 360.0/n degrees.
  4. 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.
  5. 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.

If you would like more of a challenge, try these additional exercises:

  1. Write a function called drawSpiral that takes a parameter named degrees that specifies the amount in degrees to draw a spiral.
  2. Write a function called drawStar that takes a parameter named length that draws a five-sided star with the specified side length.
  3. Write a function called drawCircles that takes a parameter named count that draws the specified number of circles side-by-side.

Encapsulation

  • Encapsulation is hiding how you implement your algorithm in a function
  • Why do you think this would be beneficial?
  • When we created a square() function, we encapsulated the process for drawing a square

In [3]:
def square( t ):
    for i in range( 4 ):
        t.fd( 100 )
        t.lt( 90 )

#square( bob )
  • This allows us to reuse functionality in the future
  • It also allows us to improve the code in the future (maybe even fix bugs) and all the code that uses the function automatically gets the new and improved code
  • Encapsulation doesn't work if we aren't able to abstract away the details
  • In this case, the details are the specific turtle doing the drawing
  • We could use the same function with a different turtle and it would still work
  • These are two of the big time terms in programming, and this course course specifically

Generalization

  • Another way to view abstraction, is to think of it as generalization
  • When we generalize, we get away from the details and think about the bigger concepts
  • For example, we can generalize the square() function to use any distance

In [4]:
def square( t, length ):
    for i in range( 4 ):
        t.fd( 100, length )
        t.lt( 90 )

#square( bob, 100 )
  • We can now draw a square of any size
  • We can even generalize it further to draw a polygon with any number of sides

In [5]:
def polygon( t, n, length ):
    # Calculate the angle to turn for each side
    angle = 360.0 / n
    # Draw each side
    for i in range( n ):
        t.fd( length )
        t.lt( angle )

#polygon( bob, 7, 70 )

Interface design

  • When we generalize a function, we need to think hard about how a user will call the function
  • We want to make it "as simple as possible, but not simpler"
  • This part of the function is referred to as its interface
  • It is:
    • The name of the function
    • What the function is supposed to do
    • The function arguments
    • The function's return value
  • We interact with interfaces everyday
  • Some examples of interfaces are:
    • TV remote control
    • ATM
    • Steering wheel and dashboard in a car
  • What are some other real-world examples of interfaces?

Refactoring

  • Sometimes you will discover that you can generalize a function even further after you have already written it
  • When you rearrange a program (or function) to improve functionality, sometimes by improving function interfaces, it is called refactoring
  • This happens very often in software development
  • Programmers rarely, if ever, get functions and programs right the first time
  • Often, you learn something about the problem only after you start implementing your solution
  • What sort of things can you learn? Why do you learn them only after you start on the solution?
  • Software development is an iterative process
  • What does this mean? Have you experienced this in your programs?

A development plan

  • There are a variety of processes used to develop software
  • One of the simplest process for novice programmers is encapsulation and generalization
  • It assumes that you have already decomposed the problem into subproblems
  • The following are the steps in the process:
    1. Design an algorithm for a small subproblem with no abstraction
    2. Encapsulate and generalize the algorithm
    3. Repeat the previous two steps for additional subproblems
    4. Combine subproblem solutions where appropriate into more general algorithms
  • This is referred to as a bottom-up approach
  • Another process goes the other way:
    1. Design a high-level algorithm for the overall problem
    2. Identify subproblems using abstract portions of the algorithm
    3. Expand the abstract portions of the algorithm into more specific algorithms
    4. Repeat the previous two steps until an algorithm is as specific and simple as possible
  • This is referred to as a top-down approach
  • Neither are perfect and both have problems, but
  • They are useful approaches for now

docstring

  • A docstring is a special type of comment at the beginning of a function that explains the interface

In [6]:
def polyline( t, length, n, angle ):
    """Draw n line segments with the given length and
    angle (in degreees) between them.  t is a turtle.
    """
    for i in range( n ):
        t.fd( length )
        t.lt( angle )
  • A user can access it in the cosole using the help function

In [7]:
help( polyline )


Help on function polyline in module __main__:

polyline(t, length, n, angle)
    Draw n line segments with the given length and
    angle (in degreees) between them.  t is a turtle.

  • It explains the essential information needed by users in a concise manner

Debugging

  • An interface is like a contract between a function and code that calls the function
  • It does not specify how that contract is implemented, just that it is
  • The contract specifies what the function will do for the caller
  • The required state values (i.e., variables) of the system before a function starts executing are called the preconditions
  • The state values of the system after a function executes are called the postconditions
  • Meeting preconditions are the responsibility of the code calling the function
  • If the preconditions are met, it is the responsibility of the function to do what it says it will and meet the postconditions
  • If the preconditions aren't met, there is no guarantee that the postconditions will be true
  • Understanding preconditions and postconditions can help you write better code and find any errors you may encounter

Exercises

  • Write a function called draw_square that takes a parameter t, which is a turtle, and then uses the turtle to draw a square.

In [8]:
def draw_square( t ):
    # INSERT YOUR CODE HERE
    print( 'Remove this line' ) # Jupyter needs a statement to compile
  • Write a function called draw_variable_square that takes a parameter t, which is a turtle, and a parameter length, which is the length of a side. The function should use the turtle to draw a square with sides of the specified length.

In [9]:
def draw_variable_square( t, length ):
    # INSERT YOUR CODE HERE
    print( 'Remove this line' ) # Jupyter needs a statement to compile
  • Write a function called draw_polygon that takes a parameter t, which is a turtle, a parameter n, which is the number of sides, and a parameter length, which is the length of a side. The function should draw a polygon with the specified number of sides of the specified length.

In [10]:
def draw_polygon( t, n, length ):
    # INSERT YOUR CODE HERE
    print( 'Remove this line' ) # Jupyter needs a statement to compile
  • Write a function called draw_circle that takes a parameter t, which is a turtle, and a parameter r, which is a radius. The function should draw a circle with the specified radius.

In [11]:
def draw_circle( t, r ):
    # INSERT YOUR CODE HERE
    print( 'Remove this line' ) # Jupyter needs a statement to compile