Göm meny
Gäller för: HT23

Temauppgift 2

Gör Pythonuppgifter 2 innan du börjar med Temauppgift 2.

Temauppgift 2 består av två delar som behandlar villkorssatser, jämförelser och sanningsvärden, användning av for- och while-loopen, sträng- och list-hantering med hjälp av loopar, samt inläsning och användning av data lagrad på fil i CSV-format.

  • Del 1: Felsökning och tolka felmeddelanden. Redovisas, se nedan.
  • Del 2: Autocomplete och autocorrect. Detta är en tillämpningsövning på det ni gjort i Pythonuppgifterna. Redovisas, se nedan.

Redovisning, inlämning och kompletteringar (Tema 1-3)

  • På sidan Redovisning hittar du information om den muntliga redovisningen, samt hur du kompletterar om du missar ett redovisningstillfälle.
  • På sidan Inlämningar via Lisam hittar du information inlämning av kod m.m.
  • Ni får antingen Godkänt eller Komplettering ges på inlämningar som hör ihop med temauppgift 1-3. Vid Komplettering får ni instruktioner för vad som ska kompletteras. På Temauppgift 3, kan ni även få Väl godkänt.

Att redovisa

Vid redovisningstillfället visar ni att ni gjort Del 1 och Del 2. Sedan ska ni förklara och redogöra för följande punkter:

  • Ge exempel från er kod på användning av villkorssatser, jämförelser och sanningsvärden, samt förklara vad de gör.
  • Ge exempel några felmeddelanden och förklara vad de betyder.
  • Ge exempel från er kod på användning av while- och/eller for-loop, samt förklara vad loopen gör.
  • Beskriva hur inläsning av ordlistan i Del 2 går till.
  • Översiktligt beskriva den strategi ni valt i Del 2 för att ge förslag på ord, samt berätta hur den ni implementerat den i er kod; var i koden görs vad?

Ni har 10 minuter på er för redovisningen. OBS! Förbered er på vad ni ska ta upp och i vilken ordning.

Att skicka in

Efter redovisning skickas koden för Del 2, Uppgift 1-2 (och 3 om ni gjorde den) in via Lisam. Inlämningen görs som en “spontan gruppinlämning”. Lägg till alla medlemmar i pargruppen till inlämningen, resultat registreras inte för personer som inte står med på inlämningen.

För att den lärare som rättar er inte ska missa er inlämning, namge er grupp i inlämningen till ert pargruppsnamn (A.1, B.3, E.1 etc). Du hittar ditt pargruppsnummer i anmälan i Lisam.

Del 1: Felsökning

Denna del kommer vara en övning i att felsöka pythonkod. I kurskatalogen /courses/729G46/kursmaterial/temauppg2/ finns en fil not_quite_right.py. Personen som har skrivit koden har inte testat sin kod, och den innehåller ett antal fel och buggar. Hen har inte heller kommenterat sin kod alls. Er uppgift är att gå igenom koden, testa den, identifiera och fixa de buggar som finns samt att klura ut vad det är tänkt att den ska göra, lägga till kommentarer och ändra namn så att koden är lättare att förstå.

Det kommer finnas flera typer av fel. Vissa av felen kommer python säga till om direkt (syntaxfel), andra fel kommer att krasha programmet medan det körs (runtimefel), och andra kommer inte att krasha alls, utan bara ge oväntade/fel resultat (logiska fel). Det räcker med andra ord inte med att ni bara kör koden tills den inte krashar längre, utan ni behöver klura ut vad det är menat att funktionen ska göra, och se till att den gör det. Var inte rädda för att lägga till, ta bort, byta namn på eller ändra delar av koden.

Ni får anta att det som funktionen tar in som argument är en lista som kan innehålla antingen andra listor eller värden, och alla värden (i både den yttre och de inre listorna) är av samma typ. En del av uppgiften är att komma på vilka listor som ska skickas in till funktionen för att testa den ordentligt. Tänk på vilka listor som utgör gränsfall och som kanske funktionen har svårt att hantera och testa med dem.

Notera de fel ni hittar i koden och rätta till dem. Lägg till beskrivande kommentarer för vad de olika delarna av funktionen gör, och ge funktionen ett nytt, mer beskrivande namn som motsvarar er uppfattning av vad den gör.

Del 2: Autocomplete

I Del 2 ska ni skriva en autocomplete-funktion i programmeringsspråket Python. Funktionen ska utgå ifrån vad användaren har skrivit och ska sedan gissa resten av ordet. Funktionen kan förstås göras i princip hur enkel eller sofistikerad som helst. Den absolut enklaste formen kanske jämför indata mot en ordlista och returnerar det “första bästa”, medan en mer avancerad metod tar hänsyn till hur vanligt förekommande ordet är, eller vilka ord användaren har skrivit tidigare.

Så här kan det se ut när användaren skriver in ett ord och får förslag:

Type word: bast
Autocompletion finished:  bastu
Type word: enhö
Autocompletion finished:  enhörning
Type word: fik
Autocompletion finished:  fiktiv
Type word: programmer
Autocompletion finished:  programmering

Förberedelser

Skapa en katalog för era filer till Temauppgift 2 i mappen ni skapade för kursen i er hemkatalog. Kopiera data-filerna från kurskatalogen (se nedan) och skapa en ny pythonfil som där ni skriver in kodskelettet nedan.

Se även till att ni har koll på det vi gick igenom kring filläsning från lektion 2.

Kodskelett

Nedan hittar ni det kodskelett som ni ska utgå ifrån. Innan ni går vidare, läs koden och försök klura ut vad som skulle hända om man körde den.

  • Var sker det första anropet till en funktion? Till vilken funktion?
  • Hur gör man för att avsluta programmet?

Kopiera och spara sedan ned kodskelettet nedan i en fil på lämplig plats i er hemkatalog.

def main():
    word = ""
    while word != "q":
        word = input("Type word: ").lower()
        suggestion = autocomplete(word)
        print("Autocompletion finished: ", suggestion)

def autocomplete(word):
    """Return autocomplete suggestions."""
    pass

main()

Datafiler

Förrutom ovanstående kod, kommer ni få tillgång till en liten samling ord- och frekvensfiler i olika konstellationer och filformat. Korpusfilerna finns i /courses/729G46/kursmaterial/temauppg2/del2.

shuffled.csv och alphabetical.csv är kommaseparerade filer där varje rad innehåller ett ord och ordets frekvens (d.v.s. hur ofta det förekommer i en viss textmängd). Nedan är ett exempel ur shuffled.csv:

.
.
spårvagn,4
armkrok,2
december,46
nattkylan,1
barhuvad,1
.
.

Filerna shuffled_words och alphabetical_words innehåller samma ord som respektive .csv-fil, men utan frekvenserna. På samma sätt innehåller shuffled_freq och alphabetical_freq samma frekvenser som återfinns i respektive .csv-fil, fast utan orden.

Ni får själva bedöma vilken eller vilka av korpusfilerna som ni behöver för att lösa uppgiften.

OBS! För filläsning ska inte pythonmodulen csv användas, utan ni ska läsa in filen med hjälp av loop och metoden readlines() som gicks igenom på lektionen

Tips till Del 2

Tips: kod-disposition

För program som rymms i en fil används oftast följande disposition i textfilen:

  1. importer (där andra moduler importeras)
  2. funktionsdefinitioner (en eller fler definitioner av den/de funktioner som används i programmet)
  3. anrop till funktioner (ett eller fler anrop till de funktioner som gör att något händer)

Nedan följer ett lite mer konkret exempel:

# högst upp finns importer
import random
import med


# sedan följer definitioner av de funktioner som används i programmet
def main():
    # eventuella förberedelser följt av eventuell huvudloop
    data = load_data("datafile")
    while True:
        # sats1
        # sats2
        # ...
        process_something1(arg1, arg2)
        # ...
        process_something2(arg3)


def load_data(filename):
    # pass används för att kunna skriva icke-kompletta funktionsdefinitioner
    # utan att få syntax-fel när man kör programmet
    pass


def process_something1(arg1, arg2):
    pass


def process_something2(arg3):
    pass


# efter definitionerna av funktioner som programmet använder görs ofta ett anrop
# till programmets huvudfunktion, alternativt flera funktioner om det är flera
# funktioner som ska köras.
#
# många gånger lägger man dessa anrop innanför en vilkorssats som endast kör
# anropen när programmet körs som ett skript (återkommer till detta senare i
# kursen)
if __name__ == '__main__':
    main()

Tips: början på en sträng och andra strängrelaterade saker

Det finns en hel del funktionalitet som hör ihop med specifika datatyper som man kan komma åt genom s.k. punktnotation. Exempel på detta är att använda .append() på en lista. Dessa kallas för metoder (man kan säga att metoder är funktioner som används på värden via punktnotation; mer om detta senare i kursen).

För strängar finns .lower() som returnerar strängen i enbart gemener (se kodskelettet). Andra metoder för strängar är:

  • str.endswith()
  • str.find()
  • str.join()
  • str.rstrip()
  • str.split()
  • str.startswith()
  • str.strip()

Med ovanstående notation refererar str till ett strängvärde. Vad dessa gör får du söka dig fram till.

Tips: problemuppdelning

För att lösa problem på ett datalogiskt sätt delar man oftast upp ett problem i mindre delproblem. Ett exempel på en problemuppdelning som kan göras är:

  1. Hitta möjliga kandidater på ordförslag
  2. Välj den bästa kandidaten

Genom att dela upp ett problem i delproblem kan man underlätta felsökning av kod genom att det blir lättare att avgöra i vilken del av koden som problemet kan finnas. Man kan t.ex. dela upp sin kod så att varje delproblem löses i en separat funktion. Resultatet från funktionen som löser ett delproblem skickas vidare till nästa funktion:

def large_problem(data1, data2):
    result1 = subproblem1(data1, data2)
    result2 = subproblem2(result1)

Tips: ladda in data

  • För att undvika att läsa in data från fil varje gång ni kör autocomplete, kan ni lägga till skicka med ett extra argument till funktionen autocomplete() som innehåller den information som behövs. Vissa ändringar jämfört med kodskeletet kan behövas.
  • Inläsning av data görs då någonstans innan ni går in i loopen som frågar användaren efter ett ord och sedan kör autocomplete()-funktionen.

Uppgifter till Del 2

Uppgift 1: Returnera förslag

När man tar sig an ett programmeringsproblem som man aldrig löst tidigare är det oftast bättre att antingen dela upp problemet i delproblem, eller lösa en enklare variant av problemet först.

I Uppgift 1 struntar vi i att försöka skriva en funktion som ger det bästa förslaget, utan koncentrerar oss på att enbart producera ett förslag.

Fundera ut vad ni vill att autocomplete ska göra

Fundera först på vilken strategi ni vill implementera. Ni kan exempelvis låta er funktion returnera det första bästa ordet som börjar på rätt bokstäver, eller låta den returnera en uppsättning förslag som användaren får välja mellan.

De begränsningar ni har att jobba med är att funktionen ska returnera något som kan skrivas ut.

En körning kan exempelvis se ut så här:

Type word: bast
Autocompletion finished:  bastu
Type word: enhö
Autocompletion finished:  enhörning
Type word: fik
Autocompletion finished:  fiktiv
Type word: programmer
Autocompletion finished:  programmering

En variant med autocomplete som returnerar fler än ett ord skulle kunna se ut så här:

Type word: bast
Autocompletion finished: bastu, bastun
Type word: enhö
Autocompletion finished: enhörning
Type word: fik
Autocompletion finished: fiktiv, fiktiva
Type word: program
Autocompletion finished: programmet, programmen

Formulera en plan/ tips på kodstruktur

Försök att formulera vilka steg som behöver göras för att funktionen göra det ni vill att den ska göra. Dra nytt av att ni är två som jobbar tillsammans; ni kan skriva på var sitt förslag och sedan be den andra personen läsa det och förklara det tillbaka till dig.

Förslagsvis kan uppdelning av funktionen autocomplete se ut på följande sätt:

  1. Läs in vald frekvensfil med hjälp av pythons inbyggda metod open och använd readlines för uppdelning av innehållet.
  2. Undersök om det finns ord som börjar på användarens ord i frekvensdatafilerna och lägg till orden som matchar i en ny lista.
  3. Returnera sedan den nya listan som förslag.

Implementera funktionen

Skriv sedan klart funktionen autocomplete() ovan så att den förutsäger slutet på ett inmatat ord enligt er valda strategi. Det är tillåtet att ändra på antalet argument som funktionen autocomplete() använder.

OBS! För filläsning ska inte pythonmodulen csv användas, utan ni ska läsa in filen med hjälp av loop och metoden readlines() som gicks igenom på lektionen

Uppgift 2: Använd frekvensinformation för att hitta förslag

Nu när ni skrivit en funktion som returnerar förslag är det dags att försöka förbättra det förslag som returneras.

I steg 2 ska ni

  1. skriva en ny autocomplete-funktion som föreslår ord baserat på ordfrekvensdata (skriv inte över er funktion från Uppgift 1)
  2. dokumentera era funktioner med funktionskommentarer (se nedan)

Skriv en helt ny funktion istället för att ändra på er existerande funktion. I er kod kan ni enkelt byta vilken funktion som används genom att kommentera bort den raden som använder den gamla funktionen, och sedan lägga till en rad som använder er nya funktion. Genom att behålla den gamla funktionen tar ni heller inte sönder den kod som som ni skrev till steg 1:

#suggestion = autocomplete(word)
suggestion = autocomplete_using_freq(word)

Ni får själva välja om ni använder en av de existerande .csv-filerna för att läsa in ord och deras ordfrekvenser, eller om ni använder er av ett fil-par (t.ex. alphabetical_words + alphabetical_freq) för att läsa in ordfrekvensdata.

Dokumentera även er kod med funktionskommentarer (se nedan).

Funktionskommentarer

Dokumentera i funktionskommentaren hur ni tänkt. Funktionskommentarer skriver man genom att med början på raden direkt efter funktionshuvudet lägga till en kommentar som börjar med tre citattecken och avslutas med tre citattecken. Se exempel nedan.

def get_most_frequent_word(document):
    """Returnera det mest frekventa ordet i document.

    Funktionen räknar ut ordfrekvenserna i strängen document och returnerar
    sedan det mest frekvent förekommande ordet. Ingen lexikal analys
    genomförs vilket alltså betyder förekomster av "grön" och "gröna" inte slås
    ihop till förekomst av samma ord."""

    # här följer sedan koden som tillhör till funktionen
    ...

En bra funktionskommentar börjar med en kort beskrivning av vad funktionen kommer göra och returnera. Enligt pythonstandard (PEP257), ska första raden i en funktionskommentar vara en mening i aktiv form. Behövs ytterligare förklaring lägger man till en tom rad och kan sedan skriva ett stycke med flera meningar.

Uppgift 3: Autocorrect (frivillig)

I Uppgift 2 letade ni bland tillgänglig data efter ord som började på det användaren hade skrivit. I Uppgift 3 ska ni istället försöka leta bland tillgänglig data efter ord som användaren försökte skriva.

För den frivilliga uppgiften ska ni:

  1. skriva ytterligare en funktion som istället för komplettera det användaren skrivit istället korrigerar det användaren skrivit
  2. dokumentera era funktioner med funktionskommentarer (se nedan)

Redigeringsavstånd istället för frekvensinformation

I del 1 handlade problemet om att hitta ord som börjar på det användaren har skrivit för att kunna fylla på med återstående bokstäver.

Här i del 2 handlar problemet istället om att hitta ord som användaren försökte skriva. Det finns olika sätt att göra detta. En variant är att anta att användanden skrivit rätt antal bokstäver, men att någon eller några bokstäver är fel.

Skriv en funktion som utgår från denna premiss, dvs skriv funktionen autocorrect() som tar in minst ett argument (användarens ord) och försök returnera en rättstavad variant av ordet.

Inläst data (t.ex. alphabetical_words) ska fortfarande användas (för att hitta rätt ord).

Exempel på autocorrect-körning:

Type word: bast
Autocompletion finished: bäst
Type word: enhö
Autocompletion finished: enhörning
Type word: fik
Autocompletion finished: fiktiv, fiktiva
Type word: program
Autocompletion finished: programmet, programmen

Vad är redigeringsavstånd?

Redigeringsavståndet mellan två strängar är hur många redigerande steg (raderingar, infogningar och substitueringar av tecken) som krävs för att transformera ett ord till ett annat. Ett vanligt mått på redigeringsavstånd är Levenshtein-avståndet.

För att beräkna av Levenshtein-avståndet mellan två ord kan ni använda en färdig funktion som finns implementerad i filen /courses/729G46/kursmaterial/temauppg2/del2/med.py.

Kopiera filen från kurskatalogen till samma katalog som er autocomplete-pythonfil ligger i. Sedan lägger ni till raden

import med

högst upp i er python-kod. Nu kan ni beräkna Levenshtein-avståndet mellan två strängar i er kod genom att anropa med.minimum_edit_distance() med de två strängar ni vill räkna på som argument:

# skriv ut redigeringsavståndet mellan kisse och katt
print(med.minimum_edit_distance('kisse', 'katt'))

# ovanstående kommer skriva ut följande strängen "4", dvs att tre redigeringar
# behövs för att ändra strängen "kisse" till strängen "katt"

Fortsatt förfining

Om ni har tid och är intresserade kan ni uttöka er lösning till något av följande:

  • Kombinera redigeringsavståndsdata med frekvensdata för att välja förslag.
  • Hitta möjliga kandidater till rättstavning även bland ord som är längre eller kortare än det användaren skrev.
  • Kombinera både autocorrect med autocomplete.

OBS! Ingen av de ovanstående förfinade varianterna krävs för att få godkänt, utan är endast exempel på möjlig fortsatt utveckling.


Sidansvarig: Johan Falkenjack
Senast uppdaterad: 2023-09-22