Starting from:

$30

Lab 5: Functions: Turtle Shape Functions

CSCI 141  Lab 5: Functions: Turtle Shape Functions

Introduction This lab introduces you to the art of writing your own functions, the concept of local variables and parameters, and the importance of documenting the behavior of the functions you write. You will write a handful of functions that draw various shapes using a turtle, and use them to make complex drawings with ease. 1 Functions This section of the handout reviews the basics of functions. Please read through it before getting started, and refer back to it if you encounter confusion or unfamiliar terminology when completing the activity. If you have any questions, your TA is there to help. Basics As we’ve seen in lecture, functions provide a way to assign a name to a given procedure, or a sequence of statements. We’ve been using (calling) functions that have been written for us since early in the course, such as print("Hello, world!") A function can take zero or more input arguments, have effects, and optionally return a value. For example, print("Hello world!") takes one or more arguments as input, has the effect of printing them to the screen, and does not return a value. 1 Writing your own functions is an extremely powerful ability, because it gives you a way to create customizable pieces of code that you can then use as building blocks for creating more complicated programs. Recall from lecture that the syntax for declaring a function looks something like this: def function_name(arg1, arg2): """An example function that demonstrates the syntax for writing your own functions.""" statement_1 statement_2 statement_3 return a_value Here’s an example program that declares a function that takes two inputs, computes their sum, and then prints the sum before returning it. The program then declares two variables and calls the function to print and return their sum. # example of a function definition: def calc_sum(num_1, num_2): """ Print, then return, the sum of num_1 and num_2 Precondition: num_1 and num_2 are numbers. """ the_sum = num_1 + num_2 print(the_sum) return the_sum a = 4 b = 6 # example call to the function: ab_sum = calc_sum(a, b) Notice that printing a value and returning it are two different things: printing is an effect that causes it to show up on the screen. Returning it means that the expression calc sum(num 1, num 2) evaluates to the resulting sum. This means when we execute the assignment statement after the function definition, the returned value pointed at by the sum will get assigned to the variable ab sum. 2 Triple-Quoted Strings Notice that the lines directly after after the function header (the line with the def keyword) contain a triple-quoted string. Enclosing a string in triple quotes is like using single or double quotes, except that newlines are allowed in triple-quoted strings. my_str = "a normal string with a newline" # will cause an error triple_quoted_string = """A string in triple-quotes""" # will not cause an error print(triple_quoted_string) # would print the following: # A string in # triple-quotes Otherwise, a triple-quoted string behaves just like any other string. Specifications When writing functions in any language, it’s standard practice to write a specification (or spec, for short): a description of what someone needs to know to use it. In particular, the spec typically includes a description of any parameters, effects, and the return value, if any, of the function. This makes it possible for other programmers to make use of your function without having to read through the function’s code to figure out how it works. This is what you’ve been doing all along when calling functions like print: you know what it does, but you don’t know how the code is written to accomplish its behavior. Docstrings In Python, the convention is to write function specifications in docstrings: triple-quoted strings that appear just after the function header (the line with the def keyword). The triple-quoted string is not technically a comment: it’s actual Python syntax; but an expression (or a value) on a line by itself has no effect when executed by the Python interpreter, so it’s presence doesn’t change the behavior of the program. Consequently, it is not a syntactic requirement of the language to include a docstring in every function. However, it is a requirement of this course 3 to include a docstring in every function you write, unless you are told otherwise. Take a look at the docstring in calc sum for an example. What information should you include in your docstrings? Generally speaking, a programmer should know the following after reading your function’s specification: • The meaning of each parameter that the function takes • Any effects that the function has • The return value (and its type), if any, of the function • Any preconditions: Assumptions that your function makes about the state of the program, or about the arguments passed to it. • Any postconditions: Assumptions that can be made once the function call is finished about the program state or return value. Note that it’s a good idea to keep specifications as concise as possible. For example, the spec for calc sum does not specify as a postcondition that the return value is the sum of the two inputs, because that’s already stated in the description. However, it is worth including the precondition that the arguments are numerical, because otherwise an error might result. A user of your function is now aware that they shouldn’t call calc sum with non-numeric arguments. If they do and an error occurs, it’s their mistake, not yours! Local Variables Notice that in the definition of calc sum, we created a new variable called the sum1 . Because it was created inside a function definition, the sum is what is known as a local variable, meaning that it doesn’t exist (or isn’t “visible”) anywhere in the program except inside the calc sum function definition. A variable’s scope refers to the set of statements in a program where it is accessible; in this case, the sum’s scope is within the calc sum function. If we tried to refer to the sum outside that indented code block, we’d get an error. Variables such as a, b, and ab sum are called global variables because once they are defined using an assignment statement, they are visible for the entire remainder of the program. 1 I didn’t call it sum because that’s already the name of a builtin function; it’s syntactically valid to create a variable with that name, but it “hides” the builtin function so you can’t use it anymore because sum now refers to a varaible. 4 Parameters Are Local Variables When defining a function, we need a way to refer to the arguments that are passed in when it’s called. Parameters serve this purpose: in the calc sum function definition, num 1 and num 2 are the function’s parameters. When the function is being executed, num 1 points to the value of the first argument and num 2 points to the value of the second one. Consequently, parameters are simply special local variables that are automatically assigned the values passed to the function as arguments. Like any other local variable, their scope is limited to the function definition to which they belong. Referring to num 1 or num 2 outside of the function definition will result in an error for the same reason that trying to refer to the sum will cause an error. 2 Shape Functions for Turtles You’ll now write some functions that will make it easier to make complicated drawings using Python’s turtle module. For now, you’ll be provided with the function header and specification, and it is your job to make sure that the function implements (or adheres to) the spec exactly and in all cases. 2.1 Setup Create a lab5 directory in your lab environment of choice. Fire up Thonny, create a new file, and save it as turtleshape.py. Write a comment at the top with author, date, and a description of the program. Download turtleshape test.py from the course webpage and save it in your lab5 directory alongside turtleshape.py. 2.2 Testing One of the many benefits of writing small, self-contained functions is the ability to test them independently of other code that uses them. Once you’ve tested a function thoroughly, you can safely use it without fear of a bug lurking somewhere inside. It’s a good idea to test functions one at a time, as you write them. Start by making calls to your function in the interactive shell (the bottom pane in Thonny) to see if they’re working correctly. Once you believe a function behaves as specified, open up turtleshape test.py, and look toward the bottom for a commented-out call to a function named test function name. For example, for draw square, the corresponding function is called test draw square. Remove 5 Figure 1: The correct output of turtleshape test.py after all functions are completed. the # from the beginning of the line to enable the test function, then hit the green Run button to run turtleshape test.py. Each of the functions draws one piece of the drawing shown in Figure 1. For example, the black squares of increasing size in the bottom left should appear exactly as in the figure once your draw square function works correctly. To make sure that everything’s set up correctly, first run the test program unmodified: you should see the drawing with only the green dot in the middle. 6 2.3 draw square In last week’s lab, you wrote a loop to make a turtle draw a square. Now, let’s do the same thing, but wrap it in a draw square function so we can draw a square with a simple function call. Header and Specification: def draw_square(t, side_length): """ Use the turtle t to draw a square with side_length. Precondition: t’s pen is down Postcondition: t’s position and orientation are the same as before """ Type the function header and specification (docstring) into turtleshape.py, then write code in the function body to make the turtle draw a square. Try out your draw square function in the interactive pane, something like this: >>> import turtle >>> scott = turtle.Turtle() >>> draw_square(scott, 100) 2.4 draw rectangle Next, write a more general function to draw a rectangle of any size. Notice that once we have a rectangle function, we can use it to draw a square by calling the rectangle function with equal side lengths. def draw_rectangle(t, width, height): """ Draw a rectangle using turtle t with size width x height Precondition: t’s pen is down Postcondition: t’s position and orientation are the same as before """ After uncommenting test draw rectangle in turtleshape test.py, the orange spiral of rectangles should appear as in Figure 1. 7 2.5 draw triangle Another way to generalize the square function would be to draw different equilateral polygons (i.e., shapes with different numbers of equal side lengths). To get started, implement a draw triangle function that draws a triangle with equal length sides: def draw_triangle(t, side_length): """ Draw an equilateral triangle using turtle t with side_length Precondition: t’s pen is down Postcondition: t’s position and orientation are the same as before """ When completed and the corresponding test function is uncommented, the purple bowtie-like figure should appear as in Figure 1. 2.6 draw polygon You’ve now figured out how to draw a square (4-sided polygon) and a triangle (3-sided polygon). Now, write a function that draws an n-sided polygon: def draw_polygon(t, side_length, num_sides): """ Draw a polygon with num_sides sides, each with length side_length using turtle t Precondition: t’s pen is down; num_sides > 2 Postcondition: t’s position and orientation are the same as before """ The red pattern with nested n-gons should appear as in the Figure when this function works correctly. 2.7 draw snowflake One of the reasons that functions are so powerful is that we can compose them; in other words, one function can call another. Now that we have a function that draws polygons, it’s pretty simple to make a function that uses it to create variations on the snowflake-like pattern from last week’s lab: 8 def draw_snowflake(t, side_length, num_sides): """ Use t to draw a snowflake made of ngon-sided polygons. The snowflake contains 10 copies of a polygon with num_sides and side_length, each drawn at a 36-degree angle from the previous one. Postcondition: t’s position and orientation are the same as before """ The teal, green, and blue snowflakes in the bottom right corner should appear as in the Figure once this function is working correctly. 2.8 teleport Finally, write a function that implements a convenient ability: teleport the turtle to a given location without drawing, but leaving the pen state unchanged afterwards. This is similar to the turtle object’s goto method, except it never draws. To accomplish this, you’ll need to pick up the pen first, then put it down only if it started out down. You may find it helpful to look at the turtle documentation for methods that could be useful here. def teleport(t, x, y): """ Move the turtle to (x, y), ensuring that nothing is drawn along the way. Postcondition: the turtle’s orientation and pen up/down state is the same as before. """ When basic movement is working, the gradient-colored grid of dots should appear. When the pen is correctly restored to its previous state, the vertical red lines should appear as in the Figure. 2.9 Screenshot Take a screenshot of the Turtle window when you have everything working, and name the file turtleshape.png. 9 3 Drawing Your final task will be to write a short program that uses your drawing functions to make some interesting drawing. Importing Local Files; Writing “Main” Functions You may have noticed that the code in turtleshape test.py calls functions from turtleshape.py. To make this possible, turtleshape test.py had to execute import turtleshape; this is just like importing a module (like math or turtle, except the module is located right in the same directory. Python looks first in the local directory for a module with name turtleshape.py, then it imports the code if it is found. What happens when importing code? Basically, it’s like pasting the imported module’s code into your file: it all gets run. So far, your turtleshape.py contains only function definitions, so importing it simply defines those functions. But if you wrote code outside the functions, it would get executed when you import turtleshape. Often, we want to separate the behavior of a file as a program versus as a module, so importing it causes functions to be defined but doesn’t run the program, but you can still run the program, e.g., with python turtleshape.py or by clicking the Run button. The way to do this is to use a so-called “main function” or “main guard”. turtleshape test.py contains an example of this. Basically, any code you want to run as a program but don’t want to execute when you import your file as a module, you can put inside the following if statement: if __name__ == "__main__": # code here will not run when this file is imported # but will run if the file is run as a program print("Hello, world!") You don’t need to worry about the details of why this happens, but it’s a good thing to remember how to do. You can always google it if you forget the syntax. A common way to use this is to have one function that contains your program code, usually called main(), and place a single call to that function inside the main guard: 10 def other_function(): """ Return the number 4 """ return 4 def main(): """ Main program: print hello, world! """ print("Hello, world!") if __name__ == "__main__": main() If this were in a file called prog.py, then running python prog.py would print ”Hello, world!”, whereas executing import prog would make other function and main available for use, but wouldn’t execute the call to main(). Make a Drawing Below all your function definitions in turtleshape.py, write some code that creates a drawing. Put your code in a main function and call it inside a main guard as illustrated above, so that running the test program (which imports your code) does not cause your main function to be called. Your code should use at least one loop and make use of at least two of the functions you wrote in this lab. Feel free to also use other functions from the turtle module. Take a screenshot of your drawing and save it as drawing.png. Your drawing should complete in under a few seconds (use turtle.tracer(0,0) and turtle.update() as in Lab 4), and should match your screenshot. Submission At this point, show the output of the test program and your drawing to your TA so you can be immediately awarded points for it. Unless you do not finish during the lab period, do not leave until your TA has verified that your output is correct. Create a zip file called lab5.zip containing turtleshape.py, turtleshape.png, and drawing.png. Upload your zip file to the Lab 5 assignment on Canvas. Even if your TA has checked you off, you still need to submit to Canvas. 11 Rubric You submitted a single zip file called lab5.zip, containing the correct files 1 The top of your program has comments including your name, date, and a short description of the program’s purpose. Comments placed throughout the code explain what the code is doing. 1 draw square works correctly 3 draw triangle works correctly 3 draw rectangle works correctly 3 draw polygon works correctly 3 draw snowflake works correctly 3 teleport works correctly 3 Your drawing code is is inside a main guard 2 Your drawing code uses at least one loop and two of the functions you wrote. 6 Your drawing code runs in under a few seconds. 2 Total 30 points 12