Skip to content

Marvin - steg 2

Programmering och problemlösning i Python. Strukturera koden i egna funktioner.

Du har jobbat allt material i “kmom03”.

Lösningar som använder konstruktionerna list/dict comprehension eller lambda funktioner godkänns inte i denna kurs.

Varje uppgift ska lösas med grundläggande Python konstruktioner som vi går igenom i kursen. Egenskapade funktioner får användas först i kmom03 och listor får användas först i kmom04.

  1. Kopiera din Marvin från föregående kursmoment och utgå från den koden.

    Terminal window
    # Flytta till kurskatalogen
    cd src
    cp -ri kmom02/marvin1/marvin.py kmom03/marvin2/
    cd kmom03/marvin2
  2. Strukturera koden:

    • Byt namn filen marvin.py till main.py och skapa filen marvin1.py.
    • main.py ska innehålla koden för att starta ditt program och while-loopen som som utgör menyn.
    • Skapa funktionen main och lägg all kod som var i filen i main()funktionen.
    • Din main fil skall sedan kalla på main funktion i blocket för villkoret if __name__ == "__main__" som du lägger längst ner i main.py.
  3. Koden för dina menyval ska nu flyttas till olika funktioner i marvin1.py. Med “koden för menyval” menas koden som utgör funktionaliteten för ett menyval, t.ex.

    marvin1.py
    elif choice == "1":
    # koden mellan kommentarerna här. Start
    name = input("What is your name? ")
    print("\nMarvin says:\n")
    print(f"Hello {name} - your awesomeness!")
    print("What can I do you for?!")
    # Slut
    elif choice == "2":

    Importera marvin1.py i main.py och strukturera om koden för de gamla menyvalen så att de läggs i en varsin funktion, med nedanstående namn. Dessa funktionerna har i tester utöver sitt menyval som tag också “marvin1” som tag.

    • Menyval 1 - greet
    • Menyval 2 - celsius_to_fahrenheit
    • Menyval 3 - points_to_grade
    • Menyval 4 - compare_numbers
    • Menyval 5 - validate_ssn
    • Menyval 6 - robber_language

    Om du har gjort några av extrauppgifterna från föregående kursmoment så kan du döpa dem till ett valfritt namn.

    Till exempel för menyval 1 blir det.

    main.py
    elif choice == "1":
    marvin1.greet()
    marvin1.py
    def greet():
    name = input("What is your name? ")
    print("\nMarvin says:\n")
    print(f"Hello {name} - your awesomeness!")
    print("What can I do you for?!")

    Alla input() och print() som används i menyvalen skall ligga i funktionen för menyvalet och inte i main funktionen.
    Det är OK att bryta ut din kod till flera mindre funktioner så länge de används i funktionen som efterfrågas.

    • Tags: struct. Det går att använda struct som tag i tester <sökväg>/kmom03 --tags=struct för att köra testerna som kollar att man har rätt filer på rätt plats.
  4. Skapa filen marvin2.py. Koden för de nya menyvalen nedanför ska du skriva i marvin2.py. Importera filen i main.py.

  5. För alla nya menyval ska dina input anrop ligga i “main.py” filen och du ska skicka dem som argument till de specifika menyvalsfunktionerna. Varje input-anrop för ett menyval motsvarar ett argument till funktionen. Funktionerna ska också returnera resultatet så att utskriften görs i main.py. Exempel:

    main.py
    ...
    elif choice == "8":
    string = input("Enter a string to randomize: ")
    print(randomize_string(string))
  6. Menyval 7 - create_ssn: Skapa de fyra sista siffrorna till ett födelsedatum. Skapa ett nytt menyval där Marvin ber om en sträng som innehåller ett födelse datum, t.ex. "910813". Din funktion ska skapa de fyra sista siffrorna till det, "910813-NNNN". För att skapa dem, använda random modulen och slumpa fram de tre första siffrorna. Använd sen Luhnalgoritmen för att räkna ut den fjärde. Den funkar nästa på samma sätt som i menyval 5.

    Algoritmen funkar på så sätt att varannan siffra multipliceras med 2 och 1, med start på första siffran i personnumret. Om något tal vid multiplikationen blir större än 9 ersätts det talet med dess siffersumma. Därefter summeras alla tal och här skiljer sig funktionen från menyval 5. Sista siffran erhålls genom att summan subtraheras från närmaste högre tiotal. Exempel med 811218-987:

    8 1 1 2 1 8 9 8 7
    * 2 1 2 1 2 1 2 1 2
    -------------------------
    ^ ^ ^ ^ ^ ^ ^ ^
    16 1 2 2 2 8 18 8 14

    Vilket blir 1 + 6 + 1 + 2 + 2 + 2 + 8 + 1 + 8 + 8 + 1 + 4 = 44, 50 - 44 = 6. Personnummret blir då 811218-9876.

    För att subtrahera från närmsta tiotal kan man använda modulo operatorn, %.

    Som du läst finns det funktionalitet som är samma mellan menyval 5 och 7. Därför ska du skapa en ny funktion som används i koden för menyval 5 och 7. Skapa create_ssn som tar emot födelsedatumet och skapar tre siffror. Skapa sen calculate_luhna_sum som tar emot en sträng med födelsedatumet och de tre siffrorna, utan -, "811218987". Funktionen ska räkna ut summan och returnera den, 44. I create_ssn använd summan och räkna ut sista siffran och returnera hela personnumret, med -, 811218-9876.

    Uppdatera sen koden för menyval 5 så att du använder calculate_luhna_sum i den koden också, för att räkna ut summan som ska kollas om den är delbar med 10. Kolla om talet är delbart med 10 i menyval 5 funktionen, inte i calculate_luhna_sum. Genom att återanvända funktioner på flera ställen kan vi få mer DRY kod.

    Exempel:

    input: "910813" output: "910813-2201".

    Eftersom tre siffror slumpas fram kommer ni inte få samma sista som i exemplet. Men om ni använder det i menyval 5 ska det vara ett giltigt personnummer

  7. Menyval 8 - randomize_string: Kasta om bokstäver. Marvin ska be dig skriva in ett ord som sedan slumpmässigt kastas om. Funktionen randomize_string ska bara returnera det nya slumpade ordet, inte ha med <orginal sträng> -->. Svaret ska sedan skrivas ut i formatet <orginal sträng> --> <slumpad sträng>, från main.py. Lösningen ska vara case-sensitive, med andra ord A != a.

    Tips, använd random modulen. Notera dock att ni fortfarande inte får använda listor och flera av funktionerna i random modulen returnerar listor. De funktionerna får ni inte använda.

    input: "Hej" output: "Hej --> jHe"
    input: "Borde inte bli samma igen" output: "Borde inte bli samma igen --> eel gn rtm dBmibo saiiane"
  8. Koden ska validera med Ruff.

    Terminal window
    # Kommandot funkar oavsett var du står i ditt repo
    uv run ruff check
  9. Testerna som finns ska passera.

    Terminal window
    # om du står i katalogen src/kmom03/marvin2 i ditt repo
    uv run tester ../../../tests/kmom03/marvin2/
    # om du står i root katalogen i ditt repo
    uv run tester tests/kmom03/marvin2/

Rätta eventuella fel som dyker upp och publicera igen. När det ser grönt ut så är du klar.

  • Menyval b1 - has_strings: Gör så Marvin kan ta emot fyra strängar, den första strängen ska jämföras med de andra tre. Kolla om den första strängen börjar med den andra, innehåller den tredje och slutar med den sista. Lösningen ska vara case-sensitive, med andra ord A != a. Tips, startswith(), endswith() Exempel:

    input: "anagram"
    input: "ana"
    input: "agr"
    input: "am" output: "Match"
    input: "isogram"
    input: "is"
    input: "gra"
    input: "m" output: "Match"
    input: "Palindrom"
    input: "par"
    input: "ind"
    input: "rom" output: "No match"
  • Menyval b2 - ssn_extended: I Sverige finns det två juridiska kön, kvinna och man, som registreras för varje individ vid födsel. Det påverkar den näst sista siffran i ens personnummer. Marvin ska kunna generera de 4 sista siffrorna i ett personnummer där tredje siffran i den avslutande delen av personnumret (NNXN) ska anpassas efter könet på personen. Siffran ska fortfarande slumpas men med tilläggskravet att om det är en kvinna ska den tredje siffra vara jämn och om det är en man ska den istället vara udda.
    Ändra parameterlistan till create_ssn så att det förutom födelsedatum (som är första parametern) även har en andra parameter för kön med default-argumentet None. Denna parameter har värdet "M" eller "m" om födelsedatumet tillhör en man och "K" eller "k" om det tillhör en kvinna. Observera att all inmatning sker i main.

    Exempel:

    input: "910813"
    input: "M" output: "910813-5531".
    input: "661204"
    input: "K" output: "661204-9247".

    Eftersom tre siffror slumpas fram (med kravet på udda/jämnt beroende på kön för tredje siffran) kommer ni inte få samma sista som i exemplet. Men om ni använder det i menyval 5 ska det vara ett giltigt personnummer

  1. Menyval B3 - find_all_indexes: Hitta alla index. Marvin ska be om två strängar, där den andra strängen är en del-sträng av den första. Funktionen ska returnera en kommaseparerad sträng med alla indexplatser där den andra strängen finns med i den första. Det ska inte vara ett kommatecken på slutet av strängen. Om strängen som skickas in som andra argument inte finns i första argumentet ska funktionen returnera en tom sträng.

    Använd sträng funktionen funktionen index() för att hitta indexplatser, notera att index() returnerar bara ett index åt gången, så även om en sträng finns med på två ställen returneras bara den första. För att komma runt detta och hitta alla index behöver ni anropa index() flera gånger och skicka med ett extra argument, start. start markera vilken index position funktionen ska börja leta från. Notera också att index funktionen lyfter ett ValueError om en söksträngen inte finns. Ni ska använda er av try-except i er funktion för att förhindra programmet från att krascha när det inträffar.

    Exempel:

    input: "axaaczaa44aa4", "a" output: "0,2,3,6,7,10,11"
    input: "axaaczaa33aa3", "aa" output: "2,6,10"
    input: "axaaczaa55aa5", "y" output: ""

Här finns övriga saker som är bra att tänka på.

  • Använd uv run tester tests/kmom03/marvin2 --tags <menyval> för att bara köra testerna för menyvalet du jobbar med. Du kan använda uv run tester tests/kmom03/marvin2 -s för att se alla taggar som finns och vilka tester de kör.
    • Varje menyval har samma tag som dess siffra för valet.
  • Gör många och små commits i ditt git repo, du kan ha ungefär 5-10 commits för en sådan här uppgift.