<jupyter_start><jupyter_text>4. Functions
> Just a quick introduction to some basic data types in Python.

- toc: true
- badges: true
- comments: true
- categories: [python]
- author: <NAME>
- sticky_rank: 4 
In the context of programming, a **function** is a named sequence of statements that performs a computation. When you define a function, you specify the name and the sequence of statements. Later, you can "call" the function by name.

 Why functions?

We know the area of a circle is: $A= πr^2$. Suppose we need to calculate the areas of three circles, we can do:

$r_1 = 20.16$

$r_2 = 9.13$

$r_3 = 11.55$

$A_1 = 3.14 * r_1^2$

$A_2 = 3.14 * r_2^2$

$A_3 = 3.14 * r_3^2$

**Question**: Is there an easier way to do this? 

**Question**: What if we change $π$ to 3.14159 instead of 3.14?

If a function `area_of_cicle(x)` is defined, then we could reuse it many times, like this<jupyter_code>a_1 = area_of_cicle(r_1)
a_2 = area_of_cicle(r_2)
a_3 = area_of_cicle(r_3)<jupyter_output><empty_output><jupyter_text>Function callsExample of a function call:<jupyter_code>type(42)<jupyter_output><empty_output><jupyter_text>The name of the function is `type`. The expression in parentheses is called the **argument** of the function. The result, for this function, is the type of the argument.It is common to say that a function "takes" an argument and "returns" a result. The result is also called the **return value**.To know more about this function, visit the documentation .<jupyter_code>type('42')
int('42')
int('Hello')
int(3.99)
int(-2.3)
float(42)
float('3.14')
str(42)
str(3.14)
abs(-100)
abs(-100, 42)
max(1, 2)
max(43, 5345, -654, 2, 0, 99999)<jupyter_output><empty_output><jupyter_text>***Exercise 01***Play with functions `round()`, `min()`, `ord()`, `chr()`. Read documentation https://docs.python.org/3/library/functions.html.    Math functionsPython has a math module that provides most of the familiar mathematical functions. A **module** is a file that contains a collection of related functions. Before we can use the functions in a module, we have to import it with an **import statement**:<jupyter_code>import math<jupyter_output><empty_output><jupyter_text>This statement creates(borrows) a **module object** named math. If you display the module object, you get some information about it:<jupyter_code>math
# or use help function
help(math)<jupyter_output><empty_output><jupyter_text>How to call a function? By using **dot**.<jupyter_code>ratio = 100
math.log10(ratio)
degrees = 45
radians = degrees / 180.0 * math.pi # math.pi is not a function. It is a constant.
math.sin(radians)<jupyter_output><empty_output><jupyter_text>***Exercise 02***From the documentation of `math` module , pick up two functions and play with them.  Add new functionsWe can define our own functions.Example:<jupyter_code>def print_lyrics():
    print("Hey Jude. Don't make it bad.")
    print("Take a sad song and make it better.")
type(print_lyrics)
print_lyrics()<jupyter_output>Hey Jude. Don't make it bad.
Take a sad song and make it better.<jupyter_text>We can use a function inside another function.<jupyter_code># Example
def repeat_lyrics():
    print_lyrics()
    print('Na - na - na - na - na, na - na - na - na')
    print_lyrics()
repeat_lyrics()<jupyter_output>Hey Jude. Don't make it bad.
Take a sad song and make it better.
Na - na - na - na - na, na - na - na - na
Hey Jude. Don't make it bad.
Take a sad song and make it better.<jupyter_text>Parameters and argumentsSome of the functions we have seen require arguments. For example, when you call `math.sin` you pass a number as an argument. Inside the function, the arguments are assigned to variables called **parameters**. Here is a definition for a function that takes an argument:<jupyter_code>def print_twice(whatever_name):
    print(whatever_name)
    print(whatever_name)
print_twice('Babson')
my_name = 'Jack'
print_twice(my_name)<jupyter_output>Jack
Jack<jupyter_text>***Exercise 03***Define a function my_abs to `print` the ***absolute*** value of any number. Note: you are not allowed to use built-in function `abs()`.  Variables and parameters are localWhen you create a variable inside a function, it is **local**, which means that **it only exists inside the function**. For example:<jupyter_code>def cat_twice(part1, part2):
    cat = part1 + part2
    print_twice(cat)<jupyter_output><empty_output><jupyter_text>This function takes two arguments, concatenates them, and prints the result twice. Here is an example that uses it:<jupyter_code>line1 = 'Bing tiddle '
line2 = 'tiddle bang.'
cat_twice(line1, line2)<jupyter_output>Bing tiddle tiddle bang.
Bing tiddle tiddle bang.<jupyter_text>However, when `cat_twice` terminates, the variable `cat` is destroyed. If we try to print it, we get an exception:<jupyter_code>print(cat)<jupyter_output><empty_output><jupyter_text>Parameters are also local. For example, outside function `print_twice`, there is no such thing as whatever_name.  Functions with return value(s) and void functions Return values:<jupyter_code>def give_me_a_break():
    str1 = 'break'
    return str1
print(give_me_a_break())
def give_me_a_break():
    str1 = 'break'
    return str1
    print('another break')
    
print(give_me_a_break())<jupyter_output><empty_output><jupyter_text>Void functions:Void functions might display something on the screen or have some other effect, but they don’t have a return value. If you assign the result to a variable, you get a special value called None.<jupyter_code>result = print_twice('Bing')
print(result)<jupyter_output><empty_output><jupyter_text>***Exercise 04***Modify the function my_abs to `return` the ***absolute*** value of any number.   Empty functionWhen we have not decided how to write this function, just use pass to make it run.<jupyter_code>def nop():
    pass<jupyter_output><empty_output><jupyter_text>We can use `pass` in other statements as well.<jupyter_code>age= int(input())
if age >= 18:
    pass # without pass, you will see error.<jupyter_output><empty_output><jupyter_text>Argument checkingLet's see an example first:<jupyter_code>abs('A')<jupyter_output><empty_output><jupyter_text>***Exercise 05***Modify the function my_abs to first only allow integers and floating numbers, then return the absolute value of any number. You may need a built-in function isinstance() .  Return more than one valueTake a game program as an example: a function move is created to return the new coordinates - nx and ny - after moving certain steps.<jupyter_code>import math
def move(x, y, step, angle):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

x, y = move(100, 100, 60, math.pi / 6)
print(x, y)<jupyter_output><empty_output><jupyter_text>***Exercise 06***Define a function quadratic(a, b, c) to solve a quadratic equation and return the values of two roots:$ax^2 + bx + c = 0$<jupyter_code>def quadratic(a, b, c):
    pass # please modify it so the function solves the quadratic equation and return two values<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10338504936530324, 'fraction_numerical': 0.02059238363892807, 'mean_word_length': 5.004233700254022, 'pattern_counts': {'":': 0, '<': 74, '<?xml version=': 0, '>': 76, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '279256', 'license': 'Apache-2.0', 'n_tokens_mistral': 2389, 'n_tokens_neox': 2267, 'n_words': 850, 'path': '_notebooks/2021-09-20-04-functions.ipynb', 'repo_name': 'OIM3640/blog', 'size': '7090'}