Fijne functies

Deze pagina bevat uitvoerbare code.

In deze opgave ga je oefenen met het schrijven van functies (de fundamentele bouwstenen van software) en het toepassen van onder andere string- en lijstbewerkingen.

De ingebouwde functies range en sum heb je in het college gezien; sla dat er zo nodig even bij op.

Zelf functies schrijven

Functies zijn de fundamentele bouwstenen van programmeren. Wat Python onderscheidt van andere programmeertalen is het gemak waarmee je eigen functies kan maken!

def dbl(x):
    """Geeft het argument twee keer terug

    Spam is heerlijk, en dbl("spam") is nog beter!
    """
    return 2 * x

Signatuur en docstring

De eerste regel van een functie wordt de signatuur genoemd. De signatuur van een functie bevat het sleutelwoord def (een afkorting van “define”), de naam van de functie, en een lijst met parameters tussen haakjes. Let op de dubbele punt waar de signatuur mee wordt afgesloten.

Direct onder de signatuur staat een string tussen drie dubbele aanhalingstekens """, dit heet een docstring (een afkorting van “documentation string”). We verwachten dat je in al je functies een docstring schrijft, zelfs in eenvoudige functies zoals dbl, zodat dit een gewoonte voor jou wordt.

Een docstring beschrijft in één zin wat de functie doet. Voegt het iets toe, dan schrijf je op de regels daaronder ook op wat er binnenkomt en wat er teruggaat, zoals hierboven.

Inspringen (indentatie)

De eerste driedubbele aanhalingstekens moeten ingesprongen worden direct onder de signatuur. De afsluitende driedubbele aanhalingstekens van de docstring springen op hetzelfde niveau in, en alle volgende code binnen de functie (de functie “body”) ook.

Voorbeeldopgave

Laten we beginnen met functies schrijven! Vergeet niet voor elk van deze functies een docstring toe te voegen die beschrijft wat je functie doet. Zie het voorbeeld tpl hieronder voor hoe je dit kan doen; deze lijkt heel erg op dbl die je al eerder hebt gezien.

Schrijf de functie tpl(x) die een getal accepteert en drie keer de waarde van dat getal teruggeeft.

Uitwerking

Deze doen we samen voor:

def tpl(x):
    """Geeft het argument drie keer terug"""
    return 3 * x


assert tpl(4) == 12
assert tpl(0) == 0

Let op de twee assertions eronder. Die horen erbij: pas als ze slagen weet je dat de functie doet wat je bedoelde. De tweede dekt een randgeval, het getal nul.

Opdrachten

Als in de opdrachten wordt gesproken over een getal, neem dan aan dat dit een integer of een float mag zijn. In alle andere gevallen wordt gezegd welk type moet worden gebruikt.

Voor elke opdracht geldt hetzelfde: schrijf een docstring, en test de functie met minstens twee assertions waarvan er één een randgeval dekt. Test elke functie voordat je doorgaat naar de volgende.

Opdracht 1: sq(x)

Schrijf de functie sq(x) die een getal x accepteert en het kwadraat van dat getal teruggeeft. (Het kwadraat van x is x keer zichzelf…)

Aanroep

Resultaat

sq(5)

25

sq(-3)

9

sq(0)

0

Schrijf minstens twee assertions, waarvan er één een randgeval dekt.

# jouw oplossing

Opdracht 2: interp(low, hi, fraction)

Schrijf de functie interp(low, hi, fraction) die de getallen low, hi en fraction accepteert en een getal teruggeeft.

fraction staat voor een fractie, een deel van een geheel dat van een laagste waarde low naar een hoogste waarde hi loopt. Bijvoorbeeld, in het geval van een taart zou dit van waarde 0 (geen taart) naar 2 (twee, hmmm!) kunnen lopen, waar fractie 0.75 gelijk staat aan 1.5 (anderhalve taart, ook fijn).

Hieruit volgt dat als fraction 0 is, low moet worden teruggegeven. Als fraction 1 is, zal hi moeten worden teruggegeven. Waarden voor fraction tussen 0 en 1 geven resultaten tussen low en hi.

Sterker nog, fraction kan een waarde onder 0 krijgen en het resultaat zal dan kleiner zijn dan low. Ook kan fraction een waarde boven 1 krijgen, en het resultaat zal dan groter zijn dan hi (puristen zouden dit waarschijnlijk extrapolatie in plaats van interpolatie noemen).

Nu het probleem bekend is zal je misschien geneigd zijn de functie op te delen in een aantal condities en if, elif en dergelijke te gebruiken. Je kan deze functie echter schrijven zonder conditionele statements. Probeer de functie te schrijven zonder if te gebruiken!

Hier zijn een paar voorbeelden om te verduidelijken hoe interp zal moeten werken:

Aanroep

Resultaat

interp(1.0, 9.0, 0.25)

3.0

een kwart op weg van 1.0 naar 9.0

interp(1.0, 3.0, 0.25)

1.5

een kwart op weg van 1.0 naar 3.0

interp(2, 12, 0.22)

4.2

22% op weg van 2 naar 12

interp(24, 42, 0)

24

0% op weg van 24 naar 42

interp(102, 117, -4.0)

42.0

-400% op weg van 102 naar 117 (vreemd!)

Tip

Als je niet zeker weet hoe je moet beginnen, kan je naar het eerste voorbeeld hierboven kijken. Hierbij geldt dat

low is 1.0
hi is 9.0
fraction is 0.25

Kijk of je kan bepalen hoe je deze drie waarden moet combineren om tot de correcte returnwaarde 3.0 te komen. Je zou kunnen beginnen met (hi - low) …

Schrijf minstens twee assertions, waarvan er één een randgeval dekt.

# jouw oplossing

In de volgende functies ga je strings gebruiken, reeksen van karakters.

Opdracht 3: checkends(s)

Schrijf een functie checkends(s) die als parameter een string s accepteert en True teruggeeft als het eerste karakter van s gelijk is aan het laatste karakter van s. Anders zal het False teruggeven. De functie checkends hoeft niet te werken met een lege string s (een string met waarde "").

boolean waarden

De functie moet geen strings teruggeven! In plaats daarvan moet het een boolean teruggeven, dus True of False, zonder aanhalingstekens.

In de editor zul je zien dat booleans een andere kleur krijgen (in het standaardkleurenschema van VSCode worden ze blauw) om aan te geven dat Python ze herkent als waarden van het type bool. Als je er per ongeluk strings van hebt gemaakt ("True" in plaats van True) dan worden ze oranje in VSCode.

Voor alle duidelijkheid, booleans en strings zijn twee heel verschillende dingen! Voor True en False heb je vrijwel altijd de booleans, dus zonder aanhalingstekens, nodig.

We geven straks een tip, maar bekijk eerst de volgende voorbeelden. Merk op dat het laatste voorbeeld de string met een enkele spatie is; dat is niet hetzelfde als de lege string, die bevat geen karakters:

Aanroep

Resultaat

checkends("niet gelijk")

False

checkends("kijk! wel gelijk")

True

checkends("q")

True

checkends(" ")

True

Zorg dat het laatste voorbeeld (de string met een enkele spatie) ook werkt in jouw functie. Zoals eerder gezegd, een lege string hoeft niet te werken.

Vergelijken

Je kan in deze functie gebruikmaken van een if-else-constructie, hier is een begin:

if s[0] == ...:
    return True
else:
    return False

Misschien kan je een oplossing vinden die helemaal geen if en else gebruikt, dit is ook goed!

De vergelijking x == 42 is een expressie waar True of False uitkomt.

Schrijf minstens twee assertions, waarvan er één een randgeval dekt.

# jouw oplossing

Opdracht 4: swap_ends(s)

Schrijf een functie swap_ends(s) die als parameter een string s accepteert en die string teruggeeft met het eerste en het laatste karakter verwisseld. Alles daartussen blijft staan.

Een string van één karakter, of een lege string, geef je ongewijzigd terug: daar valt niets te verwisselen.

Aanroep

Resultaat

swap_ends("hond")

"donh"

swap_ends("python")

"nythop"

swap_ends("ab")

"ba"

swap_ends("q")

"q"

Van achteren tellen

Met s[0] haal je het eerste karakter op en met s[-1] het laatste. Met een slice als s[1:-1] houd je alles daartussen over.

Om de lengte van een string te bepalen kan je de ingebouwde functie len(s) gebruiken. Die werkt ook voor andere typen, bijvoorbeeld lijsten.

Schrijf minstens twee assertions, waarvan er één een randgeval dekt. De string van één karakter is een goede kandidaat.

# jouw oplossing

Opdracht 5: convert_from_seconds(seconds)

Schrijf een functie convert_from_seconds(seconds) die als parameter een niet-negatieve integer seconds accepteert en een lijst met vier niet-negatieve integers teruggeeft die dat aantal seconden in een meer gebruikelijke vorm beschrijft, zodat:

  • het eerste element het aantal dagen bevat

  • het tweede element het aantal uren bevat

  • het derde element het aantal minuten bevat

  • het laatste element het aantal seconden bevat

Je zult op het volgende moeten letten:

  • 0 ≤ seconden < 60

  • 0 ≤ minuten < 60

  • 0 ≤ uren < 24

Er is geen limiet op het aantal dagen.

Aanroep

Resultaat

convert_from_seconds(610)

[0, 0, 10, 10]

convert_from_seconds(100000)

[1, 3, 46, 40]

convert_from_seconds(0)

[0, 0, 0, 0]

Hoe te beginnen? Je kan de volgende code (die vier variabelen telt) als begin gebruiken:

def convert_from_seconds(seconds):
    """Schrijf hier jouw docstring"""
    days = seconds // (24 * 60 * 60)  # het aantal dagen
    seconds = seconds % (24 * 60 * 60)  # het restant
    hours = ...
    minutes = ...
    return [days, hours, minutes, seconds]

De gedachte hier is dat wanneer je de vier waarden klaarzet ze in één keer als lijst kunnen worden teruggegeven, wat in de laatste regel gebeurt.

De regel die het aantal dagen berekent is:

    days = seconds // (24 * 60 * 60)

Het is nu aan jou om te bedenken wat de regels voor hours en minutes moeten zijn, en wat er met seconds moet gebeuren.

Je ziet in de bovenstaande code dat seconds op een gegeven moment een andere waarde aanneemt (het resultaat van een modulo % berekening). Het is mogelijk dit probleem op te lossen zonder seconds te veranderen, maar het al gaande wijzigen ervan is een handige oplossingsstrategie om stap voor stap tot de vier waarden te komen. Probeer deze strategie in de bovenstaande code verder toe te passen.

Schrijf minstens twee assertions, waarvan er één een randgeval dekt.

# jouw oplossing