# Learning Python 3 with the Linkbot/Defining Functions

## Contents

### Creating Functions[edit]

To start off this chapter I am going to give you an example of what you could do but shouldn't (so don't type it in):

```
a = 23
b = -23
if a < 0:
a = -a
if b < 0:
b = -b
if a == b:
print("The absolute values of", a, "and", b, "are equal.")
else:
print("The absolute values of", a, "and", b, "are different.")
```

with the output being:

The absolute values of 23 and 23 are equal.

The program seems a little repetitive. Programmers hate to repeat things -- that's what computers are for, after all! (Note also that finding the absolute value changed the value of the variable, which is why it is printing out 23, and not -23 in the output.) Fortunately Python allows you to create functions to remove duplication. Here is the rewritten example:

```
a = 23
b = -23
def absolute_value(n):
if n < 0:
n = -n
return n
if absolute_value(a) == absolute_value(b):
print("The absolute values of", a, "and", b, "are equal.")
else:
print("The absolute values of", a, "and", b, "are different.")
```

with the output being:

The absolute values of 23 and -23 are equal.

The key feature of this program is the `def`

statement. `def`

(short for define) starts a function definition. `def`

is
followed by the name of the function `absolute_value`

. Next comes a '(' followed by the parameter `n`

(`n`

is passed from the program into the function when the function is called). The statements after the ':' are executed when the function is used. The statements continue until either the indented statements end or a `return`

is encountered. The `return`

statement returns a value back to the place where the function was called. We already have encountered a function in our very first program, the `print`

function. Now we can make new functions.

Notice how the values of `a`

and `b`

are not changed.
Functions can be used to repeat tasks that don't return
values. Here are some examples:

```
def hello():
print("Hello")
def area(width, height):
return width * height
def print_welcome(name):
print("Welcome", name)
hello()
hello()
print_welcome("Fred")
w = 4
h = 5
print("width =", w, " height =", h, " area =", area(w, h))
```

with output being:

Hello Hello Welcome Fred width = 4 height = 5 area = 20

That example shows some more stuff that you can do with functions. Notice that you can use no arguments or two or more. Notice also when a function doesn't need to send back a value, a return is optional.

### Variables in functions[edit]

When eliminating repeated code, you often have variables in the repeated code. In Python, these are dealt with in a special way. So far all variables we have seen are global variables. Functions have a special type of variable called local variables. These variables only exist while the function is running. When a local variable has the same name as another variable (such as a global variable), the local variable hides the other. Sound confusing? Well, these next examples (which are a bit contrived) should help clear things up.

```
a = 4
def print_func():
a = 17
print("in print_func a = ", a)
print_func()
print("a = ", a)
```

When run, we will receive an output of:

in print_func a = 17 a = 4

Variable assignments inside a function do not override global variables, they exist only inside the function. Even though `a`

was assigned a new value inside the function, this newly assigned value was only relevant to `print_func`

, when
the function finishes running, and the `a`

's values is printed again, we see the originally assigned values.

Here is another more complex example.

```
a_var = 10
b_var = 15
e_var = 25
def a_func(a_var):
print("in a_func a_var = ", a_var)
b_var = 100 + a_var
d_var = 2 * a_var
print("in a_func b_var = ", b_var)
print("in a_func d_var = ", d_var)
print("in a_func e_var = ", e_var)
return b_var + 10
c_var = a_func(b_var)
print("a_var = ", a_var)
print("b_var = ", b_var)
print("c_var = ", c_var)
print("d_var = ", d_var)
```

in a_func a_var = 15 in a_func b_var = 115 in a_func d_var = 30 in a_func e_var = 25 a_var = 10 b_var = 15 c_var = 125 d_var = Traceback (most recent call last): File "C:\def2.py", line 19, in <module> print("d_var = ", d_var) NameError: name 'd_var' is not defined

In this example the variables `a_var`

, `b_var`

, and `d_var`

are all local variables when they are inside the function `a_func`

. After the statement `return b_var + 10`

is run, they all cease to exist. The variable `a_var`

is automatically a local variable since it is a parameter name. The variables `b_var`

and `d_var`

are local variables since they appear on the left of an equals sign in the function in the statements `b_var = 100 + a_var`

and `d_var = 2 * a_var`

.

Inside of the function `a_var`

has no value assigned to it. When the function is called with `c_var = a_func(b_var)`

, 15 is assigned to `a_var`

since at that point in time `b_var`

is 15, making the call to the function `a_func(15)`

. This ends up setting `a_var`

to 15 when it is inside of `a_func`

.

As you can see, once the function finishes running, the local variables
`a_var`

and `b_var`

that had hidden the global variables of the same name are gone. Then the statement `print("a_var = ", a_var)`

prints the value `10`

rather than the value `15`

since the local variable
that hid the global variable is gone.

Another thing to notice is the `NameError`

that happens at the end. This appears since the variable `d_var`

no longer exists since `a_func`

finished. All the local variables are deleted when the function exits. If you want to get something from a function, then you will have to use `return something`

.

One last thing to notice is that the value of `e_var`

remains unchanged inside `a_func`

since it is not a parameter and it never appears on the left of an equals sign inside of the function `a_func`

. When a global variable is accessed inside a function it is the global variable from the outside.

Functions allow local variables that exist only inside the function and can hide other variables that are outside the function.

### Linkbots in Functions : Making the Linkbot Move a Certain Distance[edit]

We've seen now that we can pass numbers and variables to a function in the function parameters. It turns out that you can pass just about anything into a function, including Linkbot objects. In the Chapter "Decisions", we wrote some code that could move a two-wheels linkbot around. In the code, we specified the angle to rotate the wheels, but it would be much cooler if we could tell the Linkbot to move some distance on the ground. When you're writing new functions, it's common to prototype how the function might be used before actually writing it, so lets try that now. We want our function to be used something like this:

```
driveDistance(myLinkbot, 10) # Drive a wheeled Linkbot 10 inches forward
```

Now that we're satisfied with how we want to **use** our function, now we have to worry about how to actually write it so that it does what we want it to do.
First, let us catalog what we know and what we have. So far we know about 2 functions that we can use to move motors on the Linkbot: `moveJoint()`

and `move()`

. Of the two, we have found that `move()`

is probably better for driving two-wheeled Linkbots. However, the `move()`

function takes angles as arguments. That leaves the question: How do we turn a distance into an angle?

It turns out there is an equation that you can use to figure out how many degrees a wheel has to turn to travel a certain distance. The equation is:

If you would like to see a derivation where that equation comes from, click on the "derivation" link below.

To solve this question, we can consider how wheels roll on a surface. Let us consider a wheel rolling on a surface that doesn't slip on the surface because it will make our calculations easier. Let us consider the "circumference" of a wheel. If you took a string and wrapped it all the way around a circle, the length of that string is the "circumference". The circumference of a circle with radius "r" is

The following figure illustrates a blue wheel with a green string wrapped around it. As the wheel rolls, the string unrolls off of the wheel.

As we can see from the figure, if the wheel rolls one full revolution, it travels one circumference of distance. Knowing that one revolution is 360 degrees, we can write a ratio:

So if we want to travel a distance "distance", we can do

Using this equation, if we know the wheel radius and we know what distance we want to travel, we can calculate the degrees to turn the wheel! Now we can write our function.

Note!For you math nerds out there, you might also know that the "arc-length" of a circle is
where is expressed in radians. Solving for gives
Converting to degrees, we get which is exactly the same as our previous result! |

Now, we can include that equation in our function. This allows us to re-use the function for any distance and we don't have to type in the equation over and over again.

```
import barobo
import math # So that we can use math.pi
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('abcd') # Change abcd to your Linkbot's serial ID
def driveDistance(linkbot, distance):
r = 3.5 / 2 # If you have a wheel that's not 3.5 inches in diameter, change "3.5" to the diameter of your wheel
degrees = (360) / (2 * math.pi * r) * distance
linkbot.move(degrees, 0, -degrees)
driveDistance(myLinkbot, 10) # Drives the Linkbot 10 inches forward
driveDistance(myLinkbot, -5) # Drives the Linkbot 5 inches backward
```

The program shown above uses the `driveDistance()`

function to drive a robot forward 10 inches and then backward 5 inches. You might wondering why we went through all the trouble of defining a function, which took four lines of code, when we could accomplish the same task without functions.

- Consider if the task was much more complex than just two movements. If you have to drive the robot forwards and backwards more than 4 times, you are actually saving time and code by using a function.
- Writing repeated code via copy/paste can be very hard to debug. Imagine if you copy and pasted the equation 20 times for 20 robot movements, and then you found a bug in the copy-pasted code. You would have to correct the bug in each one of the 20 pasted code blocks. If you had written a function with a bug in it, you would only have to fix the equation inside the function.
- If you are writing code for someone else to use, it would make sense to encapsulate your code in a function. Imagine if you are working with a team of people and your job is to write a function that moves the robot forward and backward, another person's job is to write a function that turns the robot, and a third person has to write a function that changes the LED color. You could then take all three functions and put them into a single program and have a robot that accurately drives a certain distance, turns, and changes LED color.

### Examples[edit]

**temperature2.py**

```
#! /usr/bin/python
#-*-coding: utf-8 -*-
# converts temperature to Fahrenheit or Celsius
def print_options():
print("Options:")
print(" 'p' print options")
print(" 'c' convert from Celsius")
print(" 'f' convert from Fahrenheit")
print(" 'q' quit the program")
def celsius_to_fahrenheit(c_temp):
return 9.0 / 5.0 * c_temp + 32
def fahrenheit_to_celsius(f_temp):
return (f_temp - 32.0) * 5.0 / 9.0
choice = "p"
while choice != "q":
if choice == "c":
c_temp = float(input("Celsius temperature: "))
print("Fahrenheit:", celsius_to_fahrenheit(c_temp))
choice = input("option: ")
elif choice == "f":
f_temp = float(input("Fahrenheit temperature: "))
print("Celsius:", fahrenheit_to_celsius(f_temp))
choice = input("option: ")
elif choice == "p": #Alternatively choice != "q": so that print when anything unexpected inputed
print_options()
choice = input("option: ")
```

Sample Run:

Options: 'p' print options 'c' convert from Celsius 'f' convert from Fahrenheit 'q' quit the program option:cCelsius temperature:30Fahrenheit: 86.0 option:fFahrenheit temperature:60Celsius: 15.5555555556 option:q

**area2.py**

```
#! /usr/bin/python
#-*-coding: utf-8 -*-
# calculates a given rectangle area
def hello():
print('Hello!')
def area(width, height):
return width * height
def print_welcome(name):
print('Welcome,', name)
def positive_input(prompt):
number = float(input(prompt))
while number <= 0:
print('Must be a positive number')
number = float(input(prompt))
return number
name = input('Your Name: ')
hello()
print_welcome(name)
print()
print('To find the area of a rectangle,')
print('enter the width and height below.')
print()
w = positive_input('Width: ')
h = positive_input('Height: ')
print('Width =', w, ' Height =', h, ' so Area =', area(w, h))
```

Sample Run:

Your Name:JoshHello! Welcome, Josh To find the area of a rectangle, enter the width and height below. Width:-4Must be a positive number Width:4Height:3Width = 4 Height = 3 so Area = 12

### Exercises[edit]

Rewrite the area2.py program from the Examples above to have a separate function for the area of a square, the area of a rectangle, and the area of a circle (`3.14 * radius**2`

). This program should include a menu interface.

```
def square(side):
return side * side
def rectangle(width , height):
return width * height
def circle(radius):
return 3.14159 * radius ** 2
def options():
print()
print("Options:")
print("s = calculate the area of a square.")
print("c = calculate the area of a circle.")
print("r = calculate the area of a rectangle.")
print("q = quit")
print()
print("This program will calculate the area of a square, circle or rectangle.")
choice = "x"
options()
while choice != "q":
choice = input("Please enter your choice: ")
if choice == "s":
side = float(input("Length of square side: "))
print("The area of this square is", square(side))
options()
elif choice == "c":
radius = float(input("Radius of the circle: "))
print("The area of the circle is", circle(radius))
options()
elif choice == "r":
width = float(input("Width of the rectangle: "))
height = float(input("Height of the rectangle: "))
print("The area of the rectangle is", rectangle(width, height))
options()
elif choice == "q":
print(" ",end="")
else:
print("Unrecognized option.")
options()
```