Uke 5 — functions / def / return ================================ Introduksjon ------------ Denne uken skal vi jobbe med funksjoner i Python. La oss først de på de innebygde funksjonene i Python. Du finner en liste 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()``, ``round()`` og ``input()``. Eksempler --------- Eksempel 1 .......... 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. 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. Som *argument* kan en funksjon ta ingen verdier, en eller flere verdier, og verdier med blandede typer, f.eks. ``int`` og ``str``, som vi ser nedenfor: Last ned filen her: :download:`eksempel_1.py`. .. literalinclude:: eksempel_1.py Eksempel 2 .......... 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 de andre funksjonene med ulike argumenter, og bruker resultatene. Last ned filen her: :download:`eksempel_2a.py`. .. literalinclude:: eksempel_2a.py Hvordan bruker vi disse tre funksjoner sammen? Skjønner du hvordan resultaten fra de andre funskjonene gjenbrukes? Her er et annet eksempel på å bruke to funksjoner sammen :download:`eksempel_2b.py`. .. literalinclude:: eksempel_2b.py Hvordan brukes den første funskjonen i den andre funksjonen? Eksempelene her også bruker "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. Eksempel 3 .......... 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: :download:`eksempel_3.py`. .. literalinclude:: eksempel_3.py Kjør koden. Ble outputen hva du hadde ventet deg? Kan du lage noen funksjoner til, for å unngå repetisjonen i print-linjene? Eksempel 4 .......... 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: :download:`eksempel_4.py`. .. literalinclude:: eksempel_4.py Her brukes ``return`` i ``poly_string()``. Prøv å endre koden slik at ``poly_string()`` printer strengen, i steden for å bruke ``return``. Hva skjer? Eksempel 5 .......... 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``. Last ned filen her: :download:`eksempel_5.py`. .. literalinclude:: eksempel_5.py Hva er forskjellen på x og y? Hvis vi skal bruke ``50`` igjen seinere i programmet, burde vi se på ``x`` eller ``y``? Eksempel 6 .......... Funksjoner uten return (de har return-verdien ``None``) brukes ofte for å gruppere instrukser som hører sammen, og skal gjenbrukes flere ganger. Nå kan vi endre på ting på et sted og vi får den endringen med der funksjonen er brukt. Om vi hadde kopiert instruksjonene flere ganger uten å bruke en funksjon måtte vi fikset hver enkel kopi. Last ned filen her: :download:`eksempel_6.py`. .. literalinclude:: eksempel_6.py Kjør koden. Hvorfor blir ikke ``ascii_cube()`` skrevet ut? .. note:: 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. Eksempel 7 .......... Her er et eksempel på hva som skjer om man ikke bruker ``return`` i funksjoner. 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: :download:`eksempel_7.py`. .. literalinclude:: eksempel_7.py Kjør koden. Hva er forskjellen på ``hello1`` og ``hello2``? Hva lagres i ``myName``? Det er ikke vanlig å ha med ``print()`` i en funksjon som skal regne ut noe. Returner verdien eller en streng i steden for. Da kan brukeren til funksjonen bestemme om det skal printes noe eller om verdien skal gjenbrukes på en annen måte. Eksempel 8 .......... Last ned filen her: :download:`eksempel_8.py`. .. literalinclude:: eksempel_8.py 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. .. warning:: Ikke bruk ``input()`` i koden din, untatt oppgave 10, fordi da vil autotestene ikke kjøre. Oppgave 1 ......... I filen ``uke_05_oppg_1.py``, skriv en funksjon som heter ``multiply_3()``. Funksjonen skal ta et argument (av typen ```int``), multiplisere dette med 3, og **returnere** resultatet. Det er ikke nødvendig å printe ut resultatet. .. code-block:: python def multiply_3(my_number): # din kode her Oppgave 2 ......... Nå skal vi prøve et likt problem som oppgave 1, men denne tiden med strings. I filen ``uke_05_oppg_2.py``, skriv en funksjon som heter ``make_question()``. Funksjonen skal ta et argument (av typen ``str``), legge til en ``"?"`` til slutten av ordet eller frasen, og **returnere** resultatet. For eksempel, argumentet ``"Hello"`` skal returnere ``"Hello?"``. Ikke ta input fra brukeren, bare lag funksjonen. Om du vil sjekke den, kan du f.eks printe den med ulike argumenter: .. code-block:: python def make_question(text): # din kode her print(make_question("Hello")) print(make_question("How are you")) Oppgave 3 ......... I filen ``uke_05_oppg_3.py``, skriv en funksjon som heter ``name_age()``. Funksjonen skal ta to argumenter: (1) navnet til en person og (2) personens alder. Navnet er en ``str`` og alderen er ``int``. Funksjonen skal returnere en streng med setningen ``"{navn} er {alder} år gammel."`` (NB: uten krøllparenteser!). For eksempel, ``name_age("Ola", 7)`` skal returnere strengen ``"Ola er 7 år gammel."`` Oppgave 4 ......... I filen ``uke_05_oppg_4.py``, skriv en funksjon som heter ``my_math()``. Funksjonen skal ta to argumenter :math:`a` og :math:`b` og skal returnere :math:`2a + b`. Oppgave 5 ......... I denne oppgaven skal vi lage våre egne funksjoner som beregner maximum, minimum og absoluttverdi av to tall. Vi skal også beregne lengden til en streng. Vi skal sammenligne disse med Pythons innebygde funksjoner ``max()``, ``min()``, ``abs()`` og ``len()``. I filen ``uke_05_oppg_5.py``, skriv kode som gjør følgende: 1. Definerer en funksjon som heter ``egen_abs()`` som beregner absoluttverdien til et tall :math:`a` uten å bruke ``abs()``. Du kan for eksempel bruke ``if``. 2. Definerer en funksjon som heter ``egen_max()`` som beregner maximum av heltallene :math:`a` og :math:`b` ifølge formelen: .. math:: \frac{a + b + | a - b |}{2}. (:math:`|a - b|` betyr absoluttverdien av :math:`a-b`.) Funksjonen skal returnere et heltall. 3. Definerer en funksjon som heter ``egen_min()`` som beregner minimum av heltallene :math:`a` og :math:`b` ifølge formelen: .. math:: \frac{a + b - | a - b |}{2}. Funksjonen skal returnere et heltall. 4. Definerer en funksjon som heter ``egen_len()`` som beregner lengde til en streng uten å bruke ``len()``. (Tips: bruk en løkke.) Prøv å kjøre koden din med noen ulike tall. Gir dine egne funksjoner riktig svar? **Ikke vurdert:** Kan du implementere din egen ``round(x, n)``? Oppgave 6 ......... I denne oppgaven skal vi fortsette med ``min()`` og ``max()``, med denne gangen skal vi identifisere den eldste av to mennesker. I filen ``uke_05_oppg_6.py`` skriv en funksjon som heter ``hvem_eldst()`` som tar som input fire argumenter: navnet på første personen (``string``), alderen på første personen (``int``), navnet på andre personen (``string``), alderen på andre personen (``int``). Funksjonen bestemmer siden hvem som er eldre og returnerer en setning som angir navn og alder på den eldste personen enligt følgende eksempel: ``hvem_eldst("Ola", 43, "Katrine", 23)`` skal returnere strengen ``"Ola er 43 år og eldst."`` Oppgave 7 ......... **Den Hydrostatiske Balansen - Del I** Trykket (P) i atmosfæren og i det indre hav kan i en første tilnærming beskrives som hydrostatisk. Trykket (i enheten Pascal), som en partikkel plassert på en dybde :math:`z` i havet er utsatt for gis av følgende formel: .. math:: P = \rho \cdot g \cdot z hvor :math:`\rho` er vanndensiteten (med enheten :math:`\text{kg} \cdot \text{m}^{-3}`), :math:`g` er tyngdekraftens akselerasjon (med enheten :math:`\text{m} \cdot \text{s}^{-2}`) og :math:`z` er partikkelens dybde (med enheten :math:`\text{m}`). SI-enheten for trykk er Pascal :math:`[\text{Pa}]`, men i meteorologi og oceangrafi blir dokk trykk vanligvis presentert i enheten decibar :math:`[\text{dbar}]`. Disse to henger sammen ifølge formelen: :math:`1\,\text{Pa} = 10^{-4}\,\text{dbar}`. I filen ``uke_05_oppg_7.py`` skriv en funksjon som heter ``hyd_pres()`` og som beregner trykket på en gitt dybde i enheten :math:`\text{dbar}`. Argumentene til funksjonen skal være: en densitetsverdi (:math:`\rho`), tyngdekraftens akselerasjon (:math:`g`) og dybden (:math:`z`). For eksempel, om vi bruker densiteten :math:`\rho = 1025\,\text{kg} \cdot \text{m}^{-3}` og tyngdeakselerasjonen :math:`g = 10\,\text{m} \cdot \text{s}^{-2}` så er trykket :math:`102.5\, \text{dbar}` på dybde :math:`100\, \text{m}`. Det betyr altså at ``hyd_pres(1025,10,100)`` skal returnere verdien :math:`102.5`. Oppgave 8 ......... I denne oppgaven skal vi fortsette med konverteringen fra *stones* og *pounds* til *kilograms* som vi gjorde uke 2 i oppgave 3, men denne gangen skal vi bruke funksjoner. I filen ``uke_05_oppg_8.py`` skal du definere følgende funksjoner: 1. ``stones_to_pounds()`` som tar som argument en vekt i *stones* og konverterer den til en vekt i *pounds* ifølge formelen: .. math:: V_{lb} = V_{st} * 14, hvor :math:`V_{lb}` er vekten i *pounds* og :math:`V_{st}` er vekten i *stones*. 1. ``stones_to_kg()`` som tar som argument en vekt i *stones* og konverterer den til en vekt i *kilogram* ifølge formelen: .. math:: V_{kg} = \frac{V_{st}}{0.15747}, hvor :math:`V_{kg}` er vekten i *kilogram* og :math:`V_{st}` er vekten i *stones*. 2. ``pounds_to_kg()`` som tar som argument en vekt i *pounds* of konverterer den til en vekt i *kilogram* ifølge formelen: .. math:: V_{kg} = \frac{V_{lb}}{2.20462}, hvor :math:`V_{kg}` er vekten i *kilogram* og :math:`V_{lb}` er vekten i *pounds*. 3. ``imperial_to_metric()`` som tar som argument en vekt i *stones* og en vekt i *pounds* og returnerer summen av disse vektene i *kilogram*. Rund resultatet til 2 desimalplasser. (TIPPS: Du kan gjerne bruke koden på nytt fra funksjonene du allerede har skrevet). Oppgave 9 ......... I denne oppgaven skal vi dra tilbake til uke_02 og lage en ramme rundt ut en haiku. Derimot, denne gangen vil vi skrive dette som en funksjon, og vi skal **ikke** bruke ``print()`` i funksjonen. I filen ``uke_05_oppg_9.py``, skriv en funksjon som heter ``draw_haiku_frame()`` som tar de tre radene i en haiku som tre argumenter og returnerer en streng med hele haikuen med høyrejustering og med en ramme av "@" runt. For eksempel, om vi printer resultatet av ``draw_haiku_frame("What a pleasure to", "Right justify a haiku", "As an exercise")`` skal vi få følgende output: .. code-block:: text @@@@@@@@@@@@@@@@@@@@@@@@@ @ What a pleasure to @ @ Right justify a haiku @ @ As an exercise @ @@@@@@@@@@@@@@@@@@@@@@@@@ (Tips: bruk ``len()`` og ``max()`` til å finne lengden av den lengste raden. Siden printer du hver rad med så mange mellomrom før raden som det er forskjell mellom den raden og den lengste raden i haikun. Husk å printe en rad med "@" før og etter haikuen og husk å printe "@" i starten og slutten av hver rad med tekst. Hvor mange tegn lengre må toppen og bunnen av rammen være enn den lengste raden i haikuen?) Oppgave 10 .......... I filen ``uke_05_oppg_10.py`` skal vi finne (den innvendige) tangeringssirkelen til en trekant. `Tangeringssirkelen `_ er den største sirkelen som får plass inne i en trekant, se bilde: .. image:: ./incircle.png :width: 300 Vi får gitt de tre hjørnene i triangelen (hver hjørne består av en x-koordinat og en y-koordinat) og så skal vi beregne mittpunkten og radiusen på tangeringssirkelen. Start med å laste ned filen :download:`uke_05_oppg_10_skeleton.py ` (du må døpe om filen til ``uke_05_oppg_10.py``): .. literalinclude:: oppgaver/uke_05_oppg_10_skeleton.py For å finne tangeringssirkelen 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. Den første funksjonen heter ``side_length()``. Bytt ut kommentaren ``# Din kode her`` med kode som beregner lengden på siden mellom punktene :math:`(x1,y1)` og :math:`(x2,y2)`. Lengden :math:`l` gis av formelen: .. math:: l = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} Tips: du kan bruke ``sqrt()`` til å regne ut kvadratrot. Neste funksjon er ``mid()``. Her skal vi beregne midtpunkten av trekanten. Vi må gjøre det hver for seg for x-koordinaten og for y-koordinaten. Midtpunktens x-koordinat gis av formelen: .. math:: x = \frac{x_1 \cdot a + x_2 \cdot b + x_3 \cdot c}{a+b+c}, og midpunktens y-koordinat gis av formelen: .. math:: y = \frac{y_1 \cdot a + y_2 \cdot b + y_3 \cdot c}{a+b+c}, hvor :math:`a` er lengden av siden som er motsatt :math:`(x_1,y_1)`, :math:`b` er lengden av siden som er motsatt :math:`(x_2,y_2)` og :math:`c` er lengden av siden som er motsatt :math:`(x_3,y_3)` (fra `wikipedia `_). Siden disse to formlene er veldig like (vi bare bytter ut x-koordinater mot y-koordinater) så kan vi bruke én funksjon for å finne både midpunktens x-koordinat og y-koordinat. Dette er hva funksjonen ``mid()`` skal gjøre. Den tar enten x-koordinatene til triangelens hjørner og sidlengdene og beregner midpunktens x-koordinat, eller y-koordinatene til triangelens hjørner og sidlengdene og beregner midpunktens y-koordinat. Funksjonen ``incircle_radius()`` skal beregne radiusen til tangenssirkelen basert på sidelengdene ``a``, ``b`` og ``c``. Radiusen :math:`r` gis av formelen: .. math:: r = \sqrt{\frac{(s-a)(s-b)(s-c)}{s}}, hvor :math:`s=(a+b+c)/2` (fra `wikipedia `_). Til slutt skal vi fylle inn ``find_incircle()``. Alle stedene der 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!