Uke 7 — list / tuple / string ================================ Denne uken avslutter vi med lister og tupler. Les https://automatetheboringstuff.com/2e/chapter4/ frem til "A Short Program: Conway’s Game of Life." Eksempler --------- Eksempel 1: Tupler .................. Tupler er litt som en liste, men bruker ``()`` isteden for ``[]``. Det er noen viktige forskjeller på tupler og lister som vi skal se på seinere. I dette eksempelet lager vi noen tupler og ser hvordan de kan brukes. Last ned filen her: :download:`eksempel_1_tuple.py`, og kjør koden. .. literalinclude:: eksempel_1_tuple.py .. _uke_07_oppgave_1: Oppgave 1 ......... I ``uke_07_oppg_1.py`` skal du skrive en funksjon som bytter ut favorittmaten til en katt. Lag en funksjon kalt ``swap_tuple()`` som tar som argument en tuple med tre strenger. Den første strengen er kattens navn. Den andre strengen er kattens personlighet. Den tredje strengen er kattens favoritmat. Du skal bytte ut favoritmaten til ``"salmon"``. Funksjonen skal returnere den oppdaterte tupelen. For eksempel, ``swap_tuple(("Princess Sparkle", "aloof", "tuna"))`` skal returnere: .. code-block:: text ("Princess Sparkle", "aloof", "salmon") Eksempel 2: Sekvenser ..................... Her er noen eksempler på lister, tupler og strenger. Alle tre kan indekseres på den samme måten, og alle tre kan brukes i for-løkker. Vi kan også konvertere mellom disse tre sekvensene. Last ned filen her: :download:`eksempel_2_sequence_types.py`, og kjør koden. Skjønner du hva som skjer? .. literalinclude:: eksempel_2_sequence_types.py .. _uke_07_oppgave_2: Oppgave 2 ......... I denne oppgaven får vi data om ukens maks temperaturer som én ``str``. For eksempel kan dataen se slik ut: .. code-block:: python "16.1 14.1 13.3 15.0 13.0 13.2 12.9" # Mandag - Søndag I ``uke_07_oppg_2.py``, skriv en funksjon som heter ``update_weather()`` og som tar en slik streng med én ukes temperaturer og lager et tupel med temperaturene konvertert til ``float`` (i samme orden). For eksempel, ``update_weather("16.1 14.1 13.3 15.0 13.0 13.2 12.9")`` skal returnere følgende tupel (husk uke 6!): .. code-block:: python (16.1, 14.1, 13.3, 15.0, 13.0, 13.2, 12.9) Skriv også en funksjon som heter ``tuesday_weather()`` og som tar en tupel med en ukes temperaturer som ``float`` og returnerer temperaturen på tirsdagen (også som en ``float``). For eksempel, ``tuesday_weather((16.1, 14.1, 13.3, 15.0, 13.0, 13.2, 12.9))`` skal returnere: .. code-block:: python 14.1 Eksempel 3a: Foranderlig / ikke foranderlig ........................................... Lister, tupler og strenger er like på mange måter, hovedforskjellen er at listene kan endres, vi kaller det for "mutable". Her er noen eksempler på forskjellen mellom lister (som kan forandres), og tupler, som er 'immutable' (som ikke kan forandres). Last ned filen her: :download:`eksempel_3a_mutable.py`, og kjør koden. Skjønner du hva som skjer? Hvorfor blir ``b`` endret etter ``a += [999, 777]``? Hvorfor blir ``b`` ikke endret etter ``a += (999, 777)``? .. literalinclude:: eksempel_3a_mutable.py Eksempel 3b: Referanser ....................... Nå skal vi bruke `visualiseringsverktøyet på pythontutor.com `_ for å se hvordan referanser fungerer i python med et konkret ekesempel. Åpne lenken ovenfor og klikk "Next" for å gå trinnvis gjennom koden. Følg nøye med på hva som skjer på hvert trinn. Hvordan er ``m`` relatert til ``a``? Hvordan forholder ``b`` seg til ``m``? Hvordan forholder ``c`` seg til ``m``? Hvilke listene er det som får *appended* den ekstra listen i linje 9? (Du kan også laste ned filen her: :download:`eksempel_3b_ref.py`, og kjøre koden på datamaskinen din uten visualiseringen.) .. literalinclude:: eksempel_3b_ref.py I dette eksempelet kan vi se at ``m`` er en liste av lister og at ``a`` er den samme listen av lister. Variabelen ``b`` er en kopi av listen ``m``, som har *referanser* til de andre listene i listen ``m``. Derimot så er ``c`` helt annerledes fra de andre: det er en ny liste, med en ny versjon av listene i listen med de samme verdiene. Listene i ``c`` referer *ikke* til de samme listene som i ``m``. .. _uke_07_oppgave_3: Oppgave 3 ......... La oss nå prøve en oppgave som viser mutabilitet og referanser, som ligner på dette eksemplet. Som før, om du blir sittende fast, kan du lese gjennom dette eksemplet igjen for å få litt hjelp. Du har et dataset som inneholder de forutsagte makstemperaturene fra mandag til lørdag denne uken (se nedenfor). Men du fant ut at den forutsagte temperaturen på torsdagen ikke var riktig. Makstemperaturen skal bli 12.0. Du vil gjerne korrigere den, men vil heller gjøre det med en kopi av originalen som lar originalen være uendret. I oppgaven ``uke_07_oppg_3.py`` skriv en funksjon som heter ``adjust_daily_temps()`` og som endrer torsdagens temperatur til ``12.0``, og returnerer det oppdaterte resultatet. Resultatet må være en *tuple* som originallisten. .. code-block:: python ( ("mandag", 16.0), ("tirsdag", 13.0), ("onsdag", 14.0), ("torsdag", 13.0), ("fredag", 15.0), ("lørdag", 13.0), ) Så med dataen ovenfor ville ``adjust_daily_temps()`` returnert følgende tupel: .. code-block:: python ( ("mandag", 16.0), ("tirsdag", 13.0), ("onsdag", 14.0), ("torsdag", 12.0), ("fredag", 15.0), ("lørdag", 13.0), ) Eksempel 4: zip() ................. ``zip()`` er en innebygd funksjon som "zipper" sammen sekvensene som blir gitt inn og returnerer en liste av tupler. En sekvens kan f.eks. være en liste, streng eller en tuple. ``zip`` tar det første elementet fra hver sekvens og legger de sammen i en tuple, og så det andre elementet i hver sekvens og legger de sammen osv. Om sekvensene har ulik lengde, er det den korteste sekvensen som bestemmer lengden til ``zip()``-outputet. Last ned filen her: :download:`eksempel_4_zip.py`, og kjør koden. .. literalinclude:: eksempel_4_zip.py .. _uke_07_oppgave_4a: Oppgave 4a .......... I denne oppgaven får vi gitt to tupler med navn, personlighet og favorittmat til to katter. I filen ``uke_07_oppg_4a.py``, skriv en funksjon som heter ``tuple_repack()`` og tar to tupler med kattinformasjon og returnerer en liste med kattenes personligheter (i samme orden). For eksempel, ``tuple_repack(("Princess Sparkle", "aloof", "tuna"), ("Mr Cat", "energetic", "cream"))`` skal returnere følgende liste .. code-block:: python ["aloof", "energetic"] *TIPS:* Du kan bruke ``zip()`` og få ut resultatet derfra. .. _uke_07_oppgave_4b: Oppgave 4b .......... I denne oppgaven blir vi igjen gitt en tuple med forutsagte temperaturer i en uke. I filen ``uke_07_oppg_4b.py``, skriv en funksjon som heter ``data_reorganize()`` og som zipper tuplen dataen til en ny tuple som inneholder dagene som en tuple og temperaturene some en annen tuple. Funksjonen skal den resulterende omorganiserte tupelen. For eksempel, om ``data_reorganize()`` blir gitt følgende tupel: .. code-block:: python ( ("mandag", 16.0), ("tirsdag", 13.0), ("onsdag", 14.0), ("torsdag", 13.0), ("fredag", 15.0), ("lørdag", 13.0), ) så skal den returnere følgende tupel: .. code-block:: python ( ('mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag'), (16.0, 13.0, 14.0, 13.0, 15.0, 13.0) ) Eksempel 5: enumerate() ........................ Den innebygde funksjonen ``enumerate()`` er et alternativ til å bruke ``range(len(myList))`` med en for-løkke for å få indeksen til elementene i listen. På hver iterasjon av løkken vil ``enumerate()`` returnere to verdier: (1) indeksen til elementet i listen og (2) selve elementet i listen. Last ned filen her: :download:`eksempel_5_enum.py`, og kjør koden. Hva er forskjellen mellom disse to? Hvilken er lettere for deg å forstå? .. literalinclude:: eksempel_5_enum.py .. _uke_07_oppgave_5: Oppgave 5 ......... Du har en database med målinger hentet fra en sonde som måler vindhastighet. Sonden registrerer vindhastighet hver time. Hver 12. time kalibreres sonden. I ``uke_07_oppg_5.py`` skriv en funksjon som heter ``calibration_readout()`` og som tar en liste med ``float`` som argument (vindhastighetene). Anta at den første verdien i listen er målet i løpet av en kalibreringstime. Funksjonen skal bruke ``enumerate()`` for å returnere listen med vindhastigheter men hvor alle verdiene som er fra kalibreringstimer er byttet ut til en tuple med første verdien ``"Calibration hour"`` og andre verdien vindhastigheten. For eksempel, om ``calibration_readout()`` brukes på følgende liste: .. code-block:: python [3.0, 3.2, 3.3, 3.2, 3.0, 3.1, 3.5, 5.0, 5.0, 4.9, 4.0, 3.0, 2.9, 2.2] så skal den returnere følgende liste: .. code-block:: python [("Calibration hour", 3.0), 3.2, 3.3, 3.2, 3.0, 3.1, 3.5, 5.0, 5.0, 4.9, 4.0, 3.0, ("Calibration hour", 2.9), 2.2] Eksempel 6: Finne feilene ......................... Her er noen eksempler på ting som kan gå feil. Last ned filen her: :download:`errors_1.py`. Før du kjør koden, se om du finner alle feil som gjør at den ikke går å kjøre. Kjør siden koden. Fant du alle feil? Endre koden så at den går å kjøre. Koderaden ``greeting_str = str(full_greeting)`` er ikke feil slik at man får en error fra Python, men den gjør ikke akkurat hva vi vil at den skal gjøre. Endre så at det blir riktig output. .. literalinclude:: errors_1.py Oppgaver -------- Oppgave 1 ......... Se :ref:`uke_07_oppgave_1` rett nedenfor ekesempelen *tupler*. Oppgave 2 ......... Se :ref:`uke_07_oppgave_2` rett nedenfor ekesempelen *sekvenser*. Oppgave 3 ......... Se :ref:`uke_07_oppgave_3` rett nedenfor ekesempelen *forandres vs ikke forandres*. Oppgave 4a .......... Se :ref:`uke_07_oppgave_4a` rett nedenfor ekesempelen ``zip()``. Oppgave 4b .......... Se :ref:`uke_07_oppgave_4b` rett nedenfor ekesempelen ``zip()``. Oppgave 5 ......... Se :ref:`uke_07_oppgave_5` rett nedenfor ekesempelen ``enumerate()``. Oppgave 6 ......... I filen ``uke_07_oppg_6.py``, skriv en funksjon som heter ``pigify()`` og som oversetter et ord fra engelsk til pig latin. Oversettelsen gjøres på følgende måte: 1. Hvis et ord begynner med en vokal, skal funksjonen legge til *-way* på slutten av ordet (e.g., apple ↦ appleway). (I denne oppgaven regner vi *ikke* y som en vokal.) 2. Hvis ordet begynner med en konsonant, skal funksjonen flytte alle bokstavene i begynnelsen av ordet frem til den første vokalen, til slutten av ordet, og legge til *-ay* etter det (e.g., banana ↦ ananabay, string ↦ ingstray). (Hvis ordet bare inneholder konsonanter så skal kun *-ay* legges til på slutten av ordet.) For eksempel, ``pigify("floor")`` skal returnere: .. code-block:: python "oorflay" Skriv også en funksjon som heter ``pigify_sentence()`` som tar en setning som argument (som en ``string``) og som returnerer tilsvarende setning men ver hvert ord er oversatt til pig latin. Du kan anta at alle setinger funksjonen blir kalt med ikke inneholder spesialtegn eller store bokstaver. For eksempel, ``pigify_sentence("alice was beginning to get very tired")`` skal returnere: .. code-block:: python "aliceway asway eginningbay otay etgay eryvay iredtay" Oppgave 7 .......... I filen ``uke_07_oppg_7.py`` skal du lage en funksjon med navnet ``render_plot()`` som tar som argument en liste med koordinater (tupler med to elementer) og som siden lager en streng som er en plot hvor det er plassert en asterisk (\*) på alle koordinatene i listen og mellomrom på alle de andre koordinatene, og som har en ramme med "#" rundt hele plotten. Plotten skal være så liten som mulig, det vil si, det skal ikke finnes med tomme rader eller kolumner utenfor hvor alle punkter (\*) er. For eksempel, ``render_plot([(2, 3), (-1, 2), (1, -1), (0, 1)])`` skal returnere følgende streng (hvor "⎵" representerer mellomrom): .. math:: \begin{matrix} \# & \# & \# & \# & \# & \# \\ \# & ⎵ & ⎵ & ⎵ & * & \# \\ \# & * & ⎵ & ⎵ & ⎵ & \# \\ \# & ⎵ & * & ⎵ & ⎵ & \# \\ \# & ⎵ & ⎵ & ⎵ & ⎵ & \# \\ \# & ⎵ & ⎵ & * & ⎵ & \# \\ \# & \# & \# & \# & \# & \# \end{matrix} Hjørnet lengst ned til venstre innenfor rammen tilsvarer koordinaten (-1, -1) (som ikke skal ha en asterisk), og hjørnet lengst opp til høyre innenfor rammen tilsvarer koordinaten (2, 3) (som skal ha en asterisk). Om vi ville brukt ``print()`` til å skrive ut strengen på skjermen ville vi fått følgende output: .. code-block:: text ###### # *# #* # # * # # # # * # ###### Tips: * Det kan være en god idé å finne ut den høyeste og laveste x- og y-verdien. Om du har koordinatene i en liste som heter ``coords`` så kan du for eksempel skrive ``xs, ys = zip(*coords)`` for å få alle x-verdiene i variabelen ``xs`` og alle y-verdiene i variabelen ``ys``. Skjønner du hvorfor dette gir x- og y-koordinatene? Det er også mulig å plukke ut x- og y-koordinatene på andre måter. Kanskje med en løkke? * Når du vet den høyeste og laveste x- og y-verdien så kan du bruke ``range()`` (med én eller flere løkker?) til å iterere igjennom alle koordinatene i plotten og på hvert sted putte enten ``"*"`` eller ``" "``. Hvilket python-konstrukt skal du bruke her for å bestemme om det skal være ``"*"`` eller ``" "``? * Du må også huske rammen rundt plotten. Vil du ta med rammen når du renderer plotten med alle koordinatene eller vil du først rendere plotten og siden legge til rammen etter det? Begge måtene er mulige. Frivillig, ikke vurdert *********************** Kan du også inkludere x-akselen som "—", og y-akselen som "|" i plotten? Så at ``print(render_plot([(2, 3), (-1, 2), (1, -1), (0, 1)]))`` ville gitt følgende output: .. code-block:: text ###### # | *# #*| # # * # #—|——# # |* # ######