Les og utforsk

Introduksjon

Denne uken skal vi jobbe med funksjoner i Python. Vi har allerede vært innom mange innebygde funksjonene i python, som len(), max(), min(), abs(), int(), str(), float(), round() og input(). Du finner en komplett liste over alle innebygde funksjoner i Pythons dokumentasjon. Det er ikke nødvendig å lese alt dette nå, men det er verdt å bli kjent med dokumentasjonen. Vårt mål denne uken er å lære hvordan vi kan lage og bruke våre egne funksjoner i Python.

Før du kjører koden, anbefales det alltid å ta deg tid til å forstå hva den gjør og hvordan den fungerer.

Syntaks for funksjoner

En funksjon er en sekvens med kommandoer man kan referere til ved hjelp av et funksjonsnavn.

# Vi definerer en funksjon som heter `eksempel_funksjon`
def eksempel_funksjon():
    print("A")
    print("B")
    print("C")

# Vi kaller eksempel_funksjon 
eksempel_funksjon()

Funksjoner er veldig nyttige i programmering. Blant annet lar de deg gjenbruke kodeblokker for å redusere repetisjon og feil du kan gjøre mens du programmerer, fordi funksjoner gjør at man kan kjøre samme blokk med kode flere ganger, uten å måtte skrive den samme koden flere ganger.

def eksempel_funksjon():
    print("A")
    print("B")
    print("C")

# Vi kaller eksempel_funksjon tre ganger
eksempel_funksjon()
eksempel_funksjon()
eksempel_funksjon()

En funksjon må være definert før den kalles

find_age() # Krasjer, funksjonen find_age er ikke definert enda

def find_age():
    print("Hvilket år ble du født?")
    birth_year = int(input())
    age = 2022 - birth_year
    print(f"Du blir {age} år i år")
Parametre, returverdi og sideeffekter

Funksjoner kan ta input i form av parametre, og kan produsere output i form av både en returverdi og sideeffekter.

# Vi definerer en funksjon som heter `dobling`
# Funksjonen har én parameter (x)
# Funksjonen har returverdi (x * 2)
# Funksjonen har sideeffekt (print skriver til skjermen)
def dobling(x):
    print("Jeg dobler nå", x)
    return x * 2

# Vi utfører noen kall til dobling-funksjonen.
# Verdiene vi gir som input kalles argumenter.
a = dobling(2)
print(f'dobling av 2 er {a}')
print()
b= dobling(3)+4 
print(f'dobling av 3 plus 4 er {b}')
print()
c = dobling(dobling(2))
print(f'dobling av dobling av 2 er {c}')

Funksjoner kan ha så mange parametre vi vil. En parameter er en variabel.

def repeat_print(string, repetitions):
    print(string * repetitions)

repeat_print("foo", 3) # skriver ut foofoofoo

def f():
    return 42

print(f()) # skriver ut 42

# Merk! Antall argumenter må matche antall parametre!
repeat_print("bar") # krasjer
print(f(1, 2)) # ville også krasjet

Når koden i en funksjon treffer return, avsluttes funksjonen.

def is_positive(x):
    print("Hello!")   # kjører
    return x > 0
    print("Goodbye!") # kjører ikke ("død kode")

print(is_positive(5))  # skriver først Hello, deretter True

Hvis en funksjon ikke utfører en return-setning, returneres den spesielle verdien None.

def a():
    print("Denne funksjonen returnerer None")
    return None

print(a()) # None

def b():
    print("Denne funksjonen har en tom return-setning")
    return
    
print(b()) # None

def c():
    print("Denne funksjonenen har ikke return-setning")

print(c()) # None

Vanlig feil: forveksling av utskrift og returverdi

def cubed(x):
    print(x**3)   # Funksjon uten retur-verdi, kun side-effekt

cubed(2)          # ser ut til å virke
print(cubed(3))   # rart (skriver også ut `None`)
print(2*cubed(4)) # Krasj!

Gjør det heller slik:

def cubed(x):
    return x**3   # Funksjonen har retur-verdi, men ingen side-effekt

cubed(2)          # ser ikke ut til å virke (hvorfor?)
print(cubed(3))   # funker!
print(2*cubed(4)) # funker!
Skop

En variabel eksisterer i ett skop basert på hvor variabelen ble definert. Hver funksjon har sitt eget skop; variabler som er definert i dette skopet kan ikke nås utenfra.

def foo(x):
    print(x)

foo(2) # skriver ut 2
print(x) # Krasjer, siden variabelen x kun var definert i foo sitt skop
def bar():
    y = 42
    print(y)

bar() # skriver ut 42
print(y) # Krasjer, siden variabelen y kun var definert i bar sitt skop

Det samme variabelnavnet kan eksistere i ulike skop. Men selv om variablene heter det samme, er de helt uavhengig av hverandre.

def f(x):
    print("Vi er i f, x =", x)
    x += 5
    return x

def g(x):
    y = f(x*2)
    print("Vi er i g, x =", x)
    z = f(x*3)
    print("Vi er i g, x =", x)
    return y + z

print(g(2))

Det kan eksistere flere skop samtidig når koden kjører.

x = "x i globalt skop"
y = "y i globalt skop"

def f():
    y = "y i lokalt skop"
    z = "z i lokalt skop"
    print(x)
    print(y)
    print(z)

f()

Hold tungen rett i munnen og regn ut hva svaret blir før du kjører koden under. Ta notater på papir for å holde styr på hva som foregår.

def f(x):
    print("Vi er i f, x =", x)
    x += 7
    return round(x / 3)

def g(x):
    x *= 10
    return 2 * f(x)

def h(x):
    x += 3
    return f(x+4) + g(x)

print(h(f(1)))
Importering

Noen funksjoner er innebygd i Python, men likevel ikke umiddelbart tilgjengelig; må de importeres fra det som kalles standardbiblioteket.

print(factorial(4)) # vi håper på 4*3*2*1 = 24
# krasjer fordi factorial ikke er importert
#Her henter vi en funksjon for å regne ut fakultet fra math-bibliotektet
from math import factorial
print(factorial(4)) # 24

math-biblioteket inneholder mange nyttige funksjoner for å utføre matematiske beregninger. La oss se et eksempel hvor vi bruker funksjoner fra dette biblioteket for å regne ut lengden til hypotenusen i en rettvinklet trekant."

# Her henter vi en funksjon for å regne ut kvadratrøtter
from math import sqrt

def hypotenus(katet_1, katet_2):
    """Beregn hypotenusen fra to kateter"""
    hyp = sqrt(katet_1 ** 2 + katet_2 ** 2)
    return hyp


common_text = "Lengden av hypotenusen på den rettvinklete trekanten med katetlengdene"
print(common_text, "4 og 1 er", hypotenus(4, 1))

a = 3
b = 4
print(f"{common_text} {a} og {b} er {hypotenus(a, b)}")

c = 7
d = 2
print(f"{common_text} {c} og {d} er {hypotenus(c, d)}")
Turtle
Her er eksempler på de mange måtene vi kan bruke funksjoner til å tegne enkle eller kompleksere former i turtle-modulen.

Eksperimenter med å justere verdiene til de forskjellige variablene for å se hvordan dette endrer tegningen din for hvilken funksjon du velger å kalle.

from math import sin, cos
import turtle as t

t.speed(0)  # adjust speed of turtle, 0 is fastest, 1 is slowest
t.hideturtle()

# single shape
def square(sidelength):
    for i in range(4):
        t.forward(sidelength)
        t.right(90)


def star(num_points, sidelength):
    ext_angle = 180 - (180 / num_points)
    for i in range(num_points + 1):
        t.forward(sidelength)
        t.right(ext_angle)


def triangle(sidelength):
    for i in range(3):
        t.forward(sidelength)
        t.left(120)


# chained function to draw many shapes
def many_squares(num_of_shapes):
    for i in range(num_of_shapes):
        t.setheading(5 * i)  # rotate by 5 degrees further with each iteration
        square(80)  # draw square in rotated position


def spiral_squares(num_of_shapes, sidelength):
    for i in range(num_of_shapes):
        t.setx(0)
        t.sety(0)
        t.setheading(5 * i)  # rotate by 5 degrees further with each iteration
        square(sidelength)  # draw square in rotated position
        sidelength += 5


def spiral_stars(num_of_shapes, sidelength):
    t.color("violet", "light blue")
    t.begin_fill()
    for i in range(num_of_shapes):
        t.setx(0)
        t.sety(0)
        t.setheading(5 * i)
        star(5, sidelength)
        sidelength += 5
    t.end_fill()


# draw more complex shape
def trefoil():  # amplitude, angular displacement
    t.screensize(400, 400, "#005744")  # set background color
    t.pensize(3)  # set width of pen stroke

    # experiment with changing these variables - how does the drawing change?
    s = 0
    a = 80
    k = 3
    r = 40
    linework = "#019879"

    # draw expanding trefoil
    t.penup()
    for i in range(0, 1500):
        t.color(linework)
        s += 0.01
        a += 0.05

        x = a * cos(k * s) * cos(s)
        y = a * cos(k * s) * sin(s)

        t.goto(x, y)
        t.pendown()

    # draw circle
    t.penup()
    for i in range(0, 620):
        t.color(linework)
        s += 0.01
        x = r * cos(s)
        y = r * sin(s)

        t.goto(x, y)
        t.pendown()


#### CALL FUNCTION ###

# many_squares(60)
# spiral_stars(60, 5)
trefoil()

t.done()