<jupyter_start><jupyter_text>Python Fundamentals 9: FunctionsThis is yet another part of Python that we haven't touched on a great deal but used an awful lot so far. You can think of a function as a block of code that runs together whenever we call its name. We can pass variables/data to functions to use and these pieces of information are called _parameters._ A function can do things and return nothing or return something, which could be any data type (string, number, boolean, list, ...).<jupyter_code># Example- hello world from a function

# Define the function
def my_first_function():
    print("Hello world, from my_first_function!")
    
# Call the function to get it to run
my_first_function()
# Example - hello to me from a function

# Define the function
def say_hello(name) :
    print("Hello, {}!".format(name)) # Notice this .format() is similar to using f"..."
    
say_hello("Benedikt")<jupyter_output>Hello, Benedikt!<jupyter_text>In the second example, we saw a function `say_hello()` receive a parameter `name`, which ended up being a string (someone's name). We can give functions any parameters (also sometimes called arguments) it needs to run the code we've written. We can even have more than one parameter being passed to a function, and we don't even need to specify how many arguments we provide (this is a useful attribute of Python functions).<jupyter_code># Example - unknown number of arguments

def fruit(*fruits) :
    print("The fruits you provided are:")
    for x in fruits :
          print(x)
    
fruit("apple", "orange", "banana")<jupyter_output>The fruits you provided are:
apple
orange
banana<jupyter_text>Another very handy feature of functions in Python is the ability to assign a default parameter value. That means if we don't give the function anything as a parameter, it knows to default to the value we gave it.<jupyter_code># Example - default parameter value

def drinking_age(country = "the UK", age = 18) :
    if age > 0:
        print(f"The legal drinking age in {country} is {age}.")
    else :
        print(f"There is no legal drinking age in {country}.")
    
drinking_age("Belgium", 0)
drinking_age("United States", 21)
drinking_age()<jupyter_output>There is no legal drinking age in Belgium.
The legal drinking age in United States is 21.
The legal drinking age in the UK is 18.<jupyter_text>Return ValueOften, we want to pass an input to a function and get something back out. This is called returning a value, and we use the keyword `return` to achieve this.<jupyter_code># Example - returning a result

# Function to square the number given to it
def square(x) :
    return x * x

five_squared = square(5)
print(f"Five squared is {five_squared}")<jupyter_output>Five squared is 25<jupyter_text>RecursionRecursion is both one of the hardest and one of the most useful things you can get to grips with in programming. Recursion is a common concept in maths and programming and a function being _recursive_ means a function that calls itself. This means that yuou can loop through data to reach a result while keeping your code pretty simple and lightweight.As with loops, recursion takes us close to the edge of infinite loops, so you should always think through recursive code and ensure that it will always break somewhere. In the below example, we define a function that calls itself. This function uses a variable `k` as the data, which decreases by 1 each time we recurse. Then, the recursion ends when the condition reaches 0. As I mentioned, this is a very tricky concept so try and understand this example by playing around and changing values, but don't spend too long looking at it. Come back later and try again if you don't get it straight away, and better yet try the exercises on recursion in the accompanying exercises notebook.<jupyter_code>def tri_recursion(k):
    if(k > 0):
        result = k + tri_recursion(k - 1)
        print(result)
    else:
        result = 0
    return result

print("Recursion Example Results")
print(f"Result: {tri_recursion(6)}")<jupyter_output>Recursion Example Results
1
3
6
10
15
21
Result: 21<jupyter_text>Lambda FunctionsLambda functions are very helpful little functions that start off easy to understand but prove pretty tricky to implement. Let's start with an example, the identity function. This function's purpose in life is to return the parameter given to it. Below, you'll see that function written in the usual way and then written as a lambda function.<jupyter_code># Example - Identity function

# Written normally
def identity(x) :
    return x

# As a lambda function
result = lambda x: x

print(identity("Hello"))
print(result("Hello"))<jupyter_output>Hello
Hello
{'fraction_non_alphanumeric': 0.06365765382158825, 'fraction_numerical': 0.006599957419629551, 'mean_word_length': 4.406214039125431, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, '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': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '663576', 'license': 'MIT', 'n_tokens_mistral': 1304, 'n_tokens_neox': 1235, 'n_words': 669, 'path': '09 Functions/09_Functions.ipynb', 'repo_name': 'ethankelly/PythonFundamentals', 'size': '4697'}