Uke 3 — functions / def / return

Introduksjon

Denne uken skal vi jobbe med funksjoner i Python. Enten kan man definere sine egne funksjoner eller så kan man bruke innebygde funksjoner i Python. Du finner en liste på Pythons innebygde funksjoner i Pythons dokumentasjon. Det er vel verdt å bli kjent med dokumentasjonen. Hvis du ikke kan en funksjon fra før, kan du søke den opp her og finne ut hva den gjør. Det er ikke nødvendig å lese alt dette nå, men å lese på de funksjonene du har bruk for. For nå kan det være lurt å lese på funksjonene len(), max(), min(), abs(), int(), str(), float() og input().

Eksempler

Eksempel 0

Før vi begynner med funksjoner, litt om variabler. En variabel lagrer verdier slik at vi kan bruke dem igjen seinere. Her er et lite eksempel:

Last ned filen her: eksempel_0.py.

# Difference in assigning a variable vs printing a variable

print('vvvvvvvvvvvvvvvv')
x = 50
print('================')
y = print(50)

print('^^^^^^^^^^^^^^^^')

print('The value of x is:', x)
print('The value of y is: ', y)

Hva er forskjellen på x og y?

Hvis vi skal bruke 50 igjen seinere i programmet, burde vi se på x eller y?

For å se hva som skjer når koden kjøres kan du prøve å skrive den inn her: Python Tutor.

Eksempel 1

Funksjoner gjør at man kan kjøre samme blokk med kode flere ganger, uten å måtte skrive den samme koden flere ganger. Her er et eksempel på kode hvor vi bruker funksjoner. I koden ser du at vi ikke nødvendigvis må kalle funksjonene i samme rekkefølge som de er definert.

Eksempelet bruker også «docstrings» (documentation strings). Dette er teksten som står inne i trippel-quotes """ helt i begynnelsen av en funksjon. Docstrings forklarer hva funksjonen gjør og kan brukes av andre programmer. Prøv for eksempel å muse over funksjonsnavnene i VScode.

Last ned filen her: eksempel_1.py.

print('Hello, Alice! How are you?')
print('Hello, Bob! How are you?')
print('Hello, Carol! How are you?')
print('Hello, Dan! How are you?')


# We can do the same thing using a function instead
def hello(name):
    """Greet a person nicely"""
    text = 'Hello, ' + name + '! How are you?'
    print(text)
    
hello('Eve')
hello('Fred')
hello('Grace')



def ascii_wave():
    """Print an ascii wave"""
    print('\\'*40)
    print('/'*40)
    print('\\'*40)

def ascii_cat():
    """Print an ascii cat"""
    print(' /\_/\\')
    print('( o.o )')
    print(' > ^ <')

def ascii_square():
    """Print an ascii square"""
    print('+------+.')
    print('|`.    | `.')
    print('|  `+--+---+')
    print('|   |  |   |')
    print('+---+--+.  |')
    print(' `. |    `.|')
    print('   `+------+')

def ascii_castle():
    """Print an ascii castle"""
    print(' _   |~  _')
    print('[_]--\'--[_]')
    print('|\'|""`""|\'|')
    print('| | /^\\ | |')
    print('|_|_|I|_|_|')
    

print()
print('Here is a cat:')
ascii_cat()

print()
print('Here are two cats:')
ascii_cat()
ascii_cat()

print()
print('We can also print a castle:')
ascii_castle()


print()
print('Or a cat on a wave:')
ascii_cat()
ascii_wave()

Noen steder i koden er det brukt en ekstra \. Se for eksempel på ascii_castle(). Her står det \' noen steder, men når vi kjører programmet printes ikke \. Dette er fordi \ er en så kalt ’escape character’. Vi kan bruke \ før tegn som ellers ville ha noen annen betydning i en streng. For eksempel, om vi ikke hadde brukt \ før ' ville Python trodd at strengen var slutt ved ' og vi hadde fått en feilmelding (prøv gjerne). Men når vi putter \ før ' sier vi til Python at vi ikke mener at strengen er slutt her, uten at Python skal printe tegnet '. Siden \ er et spesielt tegn på denne måten må vi bruke \ før \ om vi vil at Python skal lese strengen som tegnet \ og ikke som en escape character. Det er derfor det står \\ på alle steder hvor vi vil printe \. Du kan lese mer om escape characters her, og her er en liste med eksempler på alle escape characters i Python 3.

Kjør koden. Hvorfor blir ikke ascii_square() skrevet ut?

Eksempel 2

Funksjoner kan også brukes til å regne ut en verdi vi vil bruke seinere. Da bruker vi return i slutten av funksjonen og sier at den «returnerer» en verdi.

Hva om vi vil lagre resultatet i en variabel? For eksempel, hvis vi vil huske navnet fra tidligere funksjon hello kan vi lagre resultatet i myName. I eksempelet er det to funksjoner: hello1 og hello2. hello1 bruker print() mens hello2 bruker return i slutten av funskjonen.

Last ned filen her: eksempel_2.py.

print('vvvvvvvvvvvvvvvv')
def hello1(name):
    """Greet a person nicely"""
    text = 'Hello, ' + name + '! How are you?'
    print(text)
    print('you do not see me until this function is called')

print('hey there, I am outside any function')

print('^^^^^^^^^^^^^^^^')
myName = hello1('Ola')
print('hello1 gives us:', myName) 
print('^^^^^^^^^^^^^^^^')

print('vvvvvvvvvvvvvvvv')
def hello2(name):
    """Greet a person nicely"""
    text = 'Hello, ' + name + '! How are you?'
    return text

print('yyyyyyyyyyyyyyyyyyy')
myName = hello2('Ola')
print('hello2 gives us: ', myName) 

print('zzzzzzzzzzzzzzzzz')

Kjør koden. Hva er forskjellen på hello1 og hello2? Hva lagres i myName?

Eksempel 3

I dette eksemplet bruker vi f-string, f-string er en formateringsteknikk for strenger i Python. Du bruker den ved å plassere en f, eller F, foran ønsket anførselstegn (’..’ eller «..»). Det fine med f-string er at du kan legge til variabler og uttrykk direkte i teksten ved å omringe de med krøllete parenteser { }.

Last ned filen her: eksempel_3.py.

print('string')
print(f'f-string')

name = 'Ola'

print('Hi ' + name + '!')
print(f'Hi {name}!')

print('5+5 =', 5+5)
print(f'5+5 = {5+5}')

Eksempel 4

I dette eksempelet skal vi bruke flere funksjoner sammen for å bestille kaffe og kjeks. Først den samme som tidligere for å si hallo, og så en ny funksjon for å bestille mat.

Legg merke til at make_order kaller order to ganger med forskjellige argumenter, og bruker begge resultatene.

Last ned filen her: eksempel_4.py.

def hello(name):
    """Greet a person nicely"""
    text = 'Hello, ' + name + '!'
    return text

def order(food, number):
    """Make an order of one type of food"""
    return f"{number} {food}s"

def make_order(coffee_number, cookie_number):
    """Make an order of coffee and cookies"""
    greeting = hello("Alice")
    coffee_order = order("coffee", coffee_number)
    cookie_order = order("cookie", cookie_number)

    print(f"{greeting}\nCan I have {coffee_order} and {cookie_order}, please?")

print("Lets call make_order(2, 10) to order 2 coffees and 10 cookies:")
make_order(2, 10)

Hvordan bruker vi disse tre funksjoner sammen? Diskuter med en andre personer hvordan resultaten fra den første og andre funskjonene brukes i den tredje funskjonen.

Her er et annet eksempel på å bruke to funksjoner sammen eksempel_4b.py.

# Change recipe to call for measurement in grams instead of cups
print('This recipe calls for 2c of flour.')

def convert_cups_to_grams(cups):
    return cups * 136


def grams_flour(grams): 
    converted = 'This recipe calls for ' + str(grams) + 'g of flour.'
    return converted


chained_output = grams_flour(convert_cups_to_grams(2))

print(chained_output)

Hvordan brukes den første funskjonen i den andre funskjonen? Se på hvordan brukes vi int og str sammen i begge.

Eksempel 5

Funksjoner kan også brukes for å gjøre matematiske beregninger. Mange ganger vil man gjøre samme beregning flere ganger. Da er det lurt å plassere koden for beregningen i en funksjon som man siden kan kalle flere ganger. Om man vil gjøre flere ulike beregninger etterpå hverandre kan man dessuten gjøre det. Her er et eksempel på kode hvor vi bruker funksjoner for å regne ut lengden til hypotenusen i en rettvinklet trekant og gjennomsnittet av tre tall.

Last ned filen her: eksempel_5.py.

# 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

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



def middelverdi(a, b, c):
    """Beregn middelverdi av tre tall"""
    mv = (a + b + c)/3
    return mv

print("Middelverdien av tallene 17, 3 og 25 er", middelverdi(17, 3, 25))
print("Middelverdien av tallene 24, 72 og 13 er", middelverdi(24, 72, 13))
resultat = middelverdi(92, 64, 89)
print("Middelverdien av tallene 92, 64 og 89 er", resultat)

# Vi kan bruke disse funksjoner sammen
print("Middelverdien av hypotenusene ovenfor er", middelverdi(hypotenus(4, 1), hypotenus(3, 4), hypotenus(7, 2)))

Kjør koden. Ble outputen hva du hadde ventet deg? Kan du lage noen funksjoner til, for å unngå repetisjonen i print-linjene?

Eksempel 6

I dette eksempelet bruker vi en funksjon for en beregning og en funksjon for å lage en streng. Vi beregner og printer andregradspolynomer.

Last ned filen her: eksempel_6.py.

def poly_string(a,b,c):
    """Create a string representation ax^2 + bx + c"""
    # Here we use an f-string.
    return f"{a} x^2 + {b} x + {c}"
    
    
def poly_value(a,b,c,x):
    """Calculate the value of ax^2 + bx + c at the point x"""
    square = a * x**2
    linear = b * x
    const  = c
    return square + linear + const
    

def pretty_print(a,b,c,x):
    """Calculate and print a quadratic polynomial"""
    poly = poly_string(a,b,c)
    value = poly_value(a,b,c,x)
    print('The value of', poly, 'for x =', x, 'is', value)
    

pretty_print(2,7,3,3)
pretty_print(4,5,2,9)

I koden har vi brukt en f-string for å lage en streng. Du trenger ikke å bruke f-strings akkurat nå, men nå vet du at de finnes. Du kan finne en kort introduksjon til f-strings her om du vil vite mer.

Her brukes return i poly_string(), i stedet for å printe direkte i funksjonen. Prøv å endre i koden så at poly_string() printer strengen, i stedet for å bruke return. Hva skjer?

Eksempel 7

Det er mulig å definere en funksjon i Python uten å bruke return. Da returnerer Python verdien None i bakgrunnen. Husk at om du vil gi verdien fra en funksjon til en annen funksjon må du bruke return. Her er noen eksempler på hva som skjer om man ikke bruker return i funksjoner.

Last ned filen her: error_1.py.

def avstand(tall_1, tall_2):
    """Regn ut avstand mellom to tall"""
    avst = abs(tall_1 - tall_2)
    print(avst)


def den_er_feil():
    """Denne funksjonen skulle ikke kjøre, men det fungerer uansett?"""
    abc = 5
    return xyz # forventer NameError... her


print('vvvvvvvvvvvvvvvv')

avst_1 = avstand(14, -9)
avst_2 = avstand(-36, 23)

print("Avstanden mellom tallene 14 og -9 er", avst_1)
print("Avstanden mellom tallene -36 og 23 er", avst_2)

print("Den største av disse avstandene er", max(avst_1, avst_2))

print('^^^^^^^^^^^^^^^^')

Når du prøver å kjøre koden får du en feilmelding.

  • Se på det vanlige outputet før feilmeldingen: hvorfor ser det slik ut?

  • Hvorfor får du error når du kjører koden?

  • Hvorfor blir ikke den siste raden skrevet ut?

  • Endre koden slik at den kjører.

  • Hvorfor fikk du ingen feilmelding fra den_er_feil()-funksjonen?

Oppgaver

I disse oppgavene skal du lage og bruke funksjoner.

Oppgave 1

I denne oppgaven skal vi fylle inn funksjonene for å finne (den innvendige) tangeringssirkelen til en trekant. Tangeringssirkelen er den største sirkelen som får plass inne i en trekant.

../_images/incircle.png

Her er det ganske mange ting vi må regne ut, men ved å bryte problemet ned til mindre funksjoner og sette dem sammen blir det enklere å løse. Start med å laste ned oppgave_1.py.

Det første vi ser er:

def side_length(x1, y1, x2, y2):
    """Returns the length of the side between the points (x1,y1) and (x2,y2)"""
    # Skriv kode her
    pass

Bytt ut kommentaren # Skriv kode her med kode som regner ut avstanden mellom (x1,y1) og (x2,y2).

Tips: du kan bruke sqrt til å regne ut kvadratrot.

Den neste funksjonen er mid. Her skal vi finne midten av trekanten, ett koordinat av gangen. Formelen (fra wikipedia) er:

\[\frac{(pointA \cdot a) + (pointB \cdot b) + (pointC \cdot c)}{a+b+c}.\]

NB: Vi regner ut senteret for x- og y-koordinatet separat. pointA er bare ett tall.

Funksjonen incircle_radius skal regne ut radiusen til tangenssirkelen basert på sidelengdene a, b og c. Igjen finner vi formelen på wikipedia

\[r = \sqrt{\frac{(s-a)(s-b)(s-c)}{s}}\]

Der \(s=(a+b+c)/2\).

Til slutt skal vi fylle inn find_incircle. Alle stedene det står 0 skal vi bruke funksjonene vi allerede har skrevet til å regne ut den riktige verdien. Her gjelder det å holde tunga rett i munnen og bruke de riktige punktene og sidelengdene.

Når du er ferdig, prøv å kjøre programmet og test noen forskjellige trekanter!

Oppgave 2

I denne oppgaven skal vi lage våre egne funksjoner som beregner maximum og minimum av to tall. Siden skal vi sammenligne disse med Pythons innebygde funksjoner max() og min().

I filen oppgave_2.py, skriv kode som gjør følgende:

  1. Definerer en funksjon som heter egen_max() som beregner maximum av heltallene \(a\) og \(b\). Det går ann å beregne uten if med denne formelen:

    \[\frac{a + b + | a - b |}{2}.\]

    (\(|a - b|\) betyr absoluttverdien av \(a-b\))

    Funksjonen skal returnere et heltall.

  2. Definerer en funksjon som heter egen_min() som beregner minimum av heltallene \(a\) og \(b\) ifølge formelen:

    \[\frac{a + b - | a - b |}{2}.\]

    Funksjonen skal returnere et heltall.

  3. Spør brukeren om to tall og skriver siden ut resultatet av egen_max(), max(), egen_min() og min() på tallene. La det være en tom linje etter inputen, og mellom resultatet for maximum og minimum.

Eksempelkjøring:

Tall 1: 39
Tall 2: 14

egen_max(39, 14) = 39
max(39, 14) = 39

egen_min(39, 14) = 14
min(39, 14) = 14

Prøv å kjøre din kode med noen ulike tall. Gir dine egne funksjoner riktig svar?

Tips

Du kan bruke Pythons innebygde funksjon abs() til å beregne absoluttverdi. Du finner informasjon om hvordan funksjonen fungerer i Pythons dokumentasjon.

Oppgave 3

I denne oppgave skal vi fortsette med konverteringen mellom Fahrenheit og Celsius vi begynte med forrige uke. Men denne gangen skal vi plassere beregningen i en funksjon.

I filen oppgave_3.py, skriv kode som gjør følgende:

  1. Definerer en funksjon som heter fahrenheit_til_celsius() som tar en temperatur i enheten Fahrenheit og returnerer en temperatur i enheten Celsius. Den matematiske formelen er:

    \[T_C = (T_F - 32) \cdot \frac{5}{9},\]

    hvor \(T_C\) er temperaturen i Celsius og \(T_F\) er temperaturen i Fahrenheit.

  2. Definerer en funksjon som heter celsius_til_fahrenheit() som tar en temperatur i enheten Celsius og returnerer en temperatur i enheten Fahrenheit. Den matematiske formelen er:

    \[T_F = \frac{9}{5} \cdot T_C + 32,\]

    hvor \(T_C\) er temperaturen i Celsius og \(T_F\) er temperaturen i Fahrenheit.

  3. Spør brukeren om en temperatur i enheten Fahrenheit og bruker fahrenheit_til_celsius() til å beregne tilsvarende temperatur i enheten Celsius. Denne temperaturen skrives ut på skjermen med én desimal.

  4. Tar verdien du fikk i steg 3 og bruker celsius_til_fahrenheit() til å beregner tilsvarende temperatur i enheten Fahrenheit. Denne temperaturen skrives ut på skjermen med èn desimal.

Eksempelkjøring:

Gi en temperatur i Fahrenheit: 225
Temperatur i Celsius: 107.2
Temperatur i Fahrenheit: 225.0

Hva skjer om du avrunder resultatet til èn desimal inne i funksjonene, så at fahrenheit_til_celsius() og celsius_til_fahrenheit() begge returnerer tall med kun èn desimal? Prøv dette med 44 og 67 Fahrenheit som input. Hvorfor blir resultatet slik?

Tips

Bruk den innebygde funksjonen round() til å bestemme antall desimaler. Du finner informasjon om hvordan funksjonen fungerer i Pythons dokumentasjon.

Oppgave 4

I denne oppgaven skal vi fortsette med tekstboksen vi holdt på med forrige uke. Denne gangen skal vi plassere tegningen av boksen i en funksjon.

I filen oppgave_4.py, skriv kode som gjør følgende:

  1. Definerer en funksjon (som du selv velger et deskriptivt navn for) som tar en streng som argument og tegner en boks runt teksten på skjermen. Det skal være ett mellomrom før og etter strengen, og en = på hver side. Fyll inn over og under tilsvarende.

  2. Spør brukeren om navn.

  3. Bruker funksjonen du definerte i 1) til å tegne en boks rund navnet på skjermen. La det være en tom linje før og etter boksen.

  4. Ønsker brukeren en god dag (velg setning selv).

Eksempelkjøring:

Hva heter du? Lina

========
= Lina =
========

Jeg håper du får en fin dag!