diff --git a/lectures/4_errors.ipynb b/lectures/4_errors.ipynb index 5b06589d..68de4b83 100644 --- a/lectures/4_errors.ipynb +++ b/lectures/4_errors.ipynb @@ -84,7 +84,7 @@ " if len(s) == 0:\n", " return\n", " print(s[0])\n", - " blaat(s[:1])\n", + " blaat(s[1:])\n", "\n", "blaat(\"test\")\n", "```\n" diff --git a/lectures/9_lussen.ipynb b/lectures/9_lussen.ipynb index a7b6efa6..95dc5b3e 100644 --- a/lectures/9_lussen.ipynb +++ b/lectures/9_lussen.ipynb @@ -1139,7 +1139,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1153,7 +1153,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.0" + "version": "3.11.2 (tags/v3.11.2:878ead1, Feb 7 2023, 16:38:35) [MSC v.1934 64 bit (AMD64)]" + }, + "vscode": { + "interpreter": { + "hash": "7321d1f7eabf315803f0fbeb0be0427ff6d045140e09c9efe7edcc325df00e23" + } } }, "nbformat": 4, diff --git a/lectures/old topics/15_lussen.ipynb b/lectures/old topics/15_lussen.ipynb index fc2bfcbb..028b507d 100644 --- a/lectures/old topics/15_lussen.ipynb +++ b/lectures/old topics/15_lussen.ipynb @@ -1689,7 +1689,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1703,7 +1703,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.0" + "version": "3.11.2 (tags/v3.11.2:878ead1, Feb 7 2023, 16:38:35) [MSC v.1934 64 bit (AMD64)]" + }, + "vscode": { + "interpreter": { + "hash": "7321d1f7eabf315803f0fbeb0be0427ff6d045140e09c9efe7edcc325df00e23" + } } }, "nbformat": 4, diff --git a/practicals/8a_list_comprehensions.md b/practicals/8a_list_comprehensions.md new file mode 100644 index 00000000..fa21379d --- /dev/null +++ b/practicals/8a_list_comprehensions.md @@ -0,0 +1,231 @@ +# List Comprehensions + + +## Voorbereiding + +Download [Python sounds](https://github.com/hanze-hbo-ict/programmeren/raw/master/problems/assets/wk4ex1.zip). + +Dit bestand moet ergens uitgepakt worden. Het bevat een aantal bestanden die allemaal in **dezelfde map** moeten staan: + +- `wk4ex1.py` (het bestand dat je gaat uitvoeren!) +- `swfaith.wav` +- `swnotry.wav` +- `spam.wav` +- `audio.py` + +### Werk vanuit deze directory! + +Je moet deze week vanuit de directory `wk4ex1` werken, je zal hiervoor moeten `cd`'en naar de map `wk4ex1`. Houd alle bestanden in `wk4ex1` bij elkaar, als je het bestand `wk4ex1.py` verplaatst zonder de andere bestanden werkt het **niet**! + +## Opdracht 1 + +Je gaat een aantal functies schrijven met behulp van list comprehensions. List comprehensions zijn een flexibele manier om een functie (of handeling, bijvoorbeeld een berekening) uit te voeren (te "mappen") op alle elementen van een lijst. + +**a.** Bekijk de functie `three_ize` vrijwel aan het begin van `wk4ex1.py`: + +```python +def three_ize(L): + """three_ize is a function that accepts a list and + returns a list of elements each three times as large. + """ + # lc, een voorbeeld van een list comprehension + lc = [3 * x for x in L] + return lc +``` + +Deze functie voert de berekening `3 * x` uit op de waarden `x` in de lijst `L`. Probeer het uit met dit voorbeeld: + +```ipython +In [1]: three_ize([13, 14, 15]) +Out[1]: [39, 42, 45] +``` + + + +**b.** Gebruik de bovenstaande functie `three_ize` als voorbeeld om de functie `scale` te schrijven met onderstaande *signature*: + +```python +def scale(L, scale_factor): + ... +``` + +Deze functie geeft een lijst terug die vergelijkbaar is met `L`, behalve dat elk element is vermenigvuldigd met `scale_factor`. Maak gebruik van list comprehensions! Een voorbeeld: + +```ipython +In [1]: scale([70, 80, 420], 0.1) +Out[1]: [7.0, 8.0, 42.0] +``` + +## Opdracht 2 + +De volgende stap is het schrijven van een functie `three_ize_by_index` met list comprehensions waar je gebruik gaat maken van de *index* (of positie) van elementen in een lijst. + +**a** Controleer dat de onderstaande functie `three_ize_by_index` in jouw bestand `wk4ex1.py` aanwezig is. + +```python +def three_ize_by_index(L): + """three_ize_by_index has the same behavior as three_ize + but it uses the INDEX of each element, instead of + using the elements themselves -- this is much more flexible! + """ + # nog een voorbeeld van een list comprehension + n = len(L) + lc = [3 * L[i] for i in range(n)] + return lc +``` + +Deze functie doet *exact hetzelfde* als `three_ize`, maar gebruikt nu de *index* van elk element. Dat wil zeggen, nu is het de **locatie** van elk element in een lijst, we noemen deze hier `i`, die steeds verandert. + +Het gebruik van een index maakt list comprehensions nog flexibeler dan wanneer we de elementen rechtstreeks gebruiken, zoals we gaan zien in de volgende opdrachten. + +**b** : `add_2` + +Gebruik de bovenstaande functie gebaseerd op *index* als richtlijn om de functie `add_2(L, m)` te schrijven met onderstaande signature: + +```python +def add_2(L, m): + ... +``` + +Deze functie krijgt twee lijsten `L` en `m` mee en geeft een enkele lijst terug die de elementen van de twee lijsten elementsgewijs bij elkaar optelt. Als de twee lijsten een andere lengte hebben, moet `add_2` een lijst teruggeven die even lang is als de *kortste* van de twee. Je kan de extra elementen van de langere lijst negeren. + +Je kan dit bijvoorbeeld doen door `min`, `len(L)` en `len(m)` te combineren. Als voorbeeld zal de regel + +```python +n = min(len(L), len(m)) +``` + +de kleinste van de lengtes van `L` en `m` aan `n` toekennen. + +Het is handig om hier een aanpak gebaseerd op *index* te gebruiken. Je kan de functie `three_ize_by_index` als voorbeeld gebruiken. Bekijk ook hoe je de onderstaande list comprehension hiervoor zou kunnen gebruiken: + +```python +lc = [L[i] + m[i] for ...] +``` + +Hieronder zie je twee voorbeelden van het gebruik van `add_2`: + +```ipython +In [1]: add_2([10, 11, 12], [20, 25, 30]) +Out[1]: [30, 36, 42] + +In [2]: add_2([10, 11], [20, 25, 30]) +Out[2]: [30, 36] +``` + +**c** : `add_3` + +Schrijf nu een vergelijkbare functie `add_3` met drie argumenten volgens de onderstaande signature: + +```python +def add_3(L, m, p): + ... +``` + +waar `L`, `m` en `p` lijsten zijn en `add_3` de som van de drie lijsten teruggeeft, maar met evenveel elementen als de *kortste* van de drie. De aanpak zal erg lijken op die voor `add_2`. + +## Opdracht 3 + +Schrijf nu een functie `add_scale_2` met de onderstaande signature: + +```python +def add_scale_2(L, m, L_scale, m_scale): + ... +``` + +Deze functie krijgt twee lijsten `L` en `m` mee en twee floating-point getallen `L_scale` en `m_scale`. Deze staan respectievelijk voor *schaalfactor voor `L`* en *schaalfactor voor `m`*. + +De functie `add_scale_2` moet een enkele lijst teruggeven die een elementsgewijze optelling is van de twee lijsten, maar *ieder vermenigvuldigd met zijn respectievelijke floating-point getallen*. Als de lijsten niet dezelfde lengte hebben, moet de functie een lijst met de lengte van de *kortste* lijst teruggeven. Je kan extra elementen negeren. + +Hier zie je twee voorbeelden: + +```ipython +In [1]: add_scale_2([10, 20, 30], [7, 8, 9], 0.1, 10) +Out[1]: [71.0, 82.0, 93.0] + +In [2]: add_scale_2([10, 20, 30], [7, 8], 0.1, 10) +Out[2]: [71.0, 82.0] +``` + +Dit zal nogal lijken op de vorige opdrachten! + +## Opdracht 4 + +**a** Bekijk deze hulpfunctie in het bestand `wk4ex1.py`: + +```python +def randomize(x, chance_of_replacing): + """randomize accepts an original value, x + and a fraction named chance_of_replacing. + + With the "chance_of_replacing" chance, it + should return a random float from -32767 to 32767. + + Otherwise, it should return x (not replacing it). + """ + r = random.uniform(0, 1) + if r < chance_of_replacing: + return random.uniform(-32768, 32767) + else: + return x +``` + +Lees de docstring en probeer de functie uit. + +Het enige dat we hier van jou vragen is om te *begrijpen* wat deze functie doet: hoe vaak wordt de *oorspronkelijke* waarde teruggegeven en hoe vaak een *willekeurige* waarde. Deze willekeurige waarde valt binnen het bereik van de drukgolven van een geluid. + +De functie geeft een willekeurige waarde terug, maar hier is een voorbeeld van een aantal keer dat de functie wordt uitgevoerd: + +```ipython +In [1]: randomize(42, .5) +Out[1]: 42 + +In [2]: randomize(42, .5) +Out[2]: 42 + +In [3]: randomize(42, .5) +Out[3]: 29209.30669767395 + +In [4]: randomize(42, .5) +Out[4]: 42 + +In [5]: randomize(42, .5) +Out[5]: 17751.221299744262 +``` + + +**b** Schrijf nu een functie `replace_some` met de volgende signature: + +```python +def replace_some(L, chance_of_replacing): + ... +``` + +De functie krijgt een lijst `L` en een floating-point getal `chance_of_replacing` mee. `replace_some` moet onafhankelijk van elkaar elk element in L vervangen (of niet vervangen) door gebruik te maken van de hulpfunctie `randomize`. + +:::{admonition} Gebruik `randomize` +:class: tip + +Gebruik `randomize` in een list comprehension, meer hoef je niet te doen! Bedenk hoe je onderstaand statement kan aanvullen (en vergeet niet om `lc` terug te geven): + +```python +lc = [randomize(..., ...) for x in L] +``` +::: + +Aangezien de functie willekeurig is, zal de uitvoer op jouw systeem niet gelijk zijn, maar probeer of het ongeveer als volgt werkt: + +```ipython +In [1]: replace_some(range(40, 50), .5) # vervang ongeveer de helft (hopelijk blijft de 42 staan!) +Out[1]: [40, 41, 42, -17461.09350529409, 44, -13989.513742241645, 46, -26247.774200304026, 48, 49] + +In [2]: replace_some(range(20, 30), .1) # vervang ongeveer een tiende (maar het is wel willekeurig: hier zijn er twee vervangen) +Out[2]: [20, 21, 16774.26240973895, 23, 24, 25, -18184.919872079583, 27, 28, 29] +``` + +Om je te helpen met testen zijn hier een paar `assert` statements om in je code over te nemen. Merk op dat de tweede assertion stelt dat het resultaat *ongelijk* is aan 42! + +```python +assert replace_some(range(40, 50), 0) == list(range(40, 50)) +assert replace_some([42], 1.0) != [42] +``` diff --git a/practicals/8b_klinkt_goed.md b/practicals/8b_klinkt_goed.md new file mode 100644 index 00000000..30a6f89c --- /dev/null +++ b/practicals/8b_klinkt_goed.md @@ -0,0 +1,462 @@ +# Klinkt goed! + +## Geluid + +Geluid is, zoals je misschien al eerder hebt gehoord, een trilling en deze trilling kan te tekenen als een golfbeweging. + +![Sound wave](images/8/wave.png) + +Hard geluid heeft een hogere amplitude dan een zacht geluid en bij een hoog geluid hoort een kleinere golflengte + +![Amplitude and frequentie](images/8/amplitude_freq.png) + +### Geluidbestanden + + +Afhankelijk van het formaat kunnen audiogegevens op veel verschillende manieren gecodeerd en opgeslagen worden. Het opslaan van een gehele geluidsgolf kost de computer teveel geheugen want hij zou elke punt in zo'n grafiek op moeten slaan. Een van de meest eenvoudige coderingen staat bekend als *pulscodemodulatie* (PCM), waarbij de geluidsgolven om de zoveel tijd worden *gesampled*. + +![Sampling](images/8/sampling.png) + +Uiteindelijk heb je een lijst van waarden in het bereik -128 tot 127 (als er 1 byte per geluidssample wordt gebruikt) of -32768 tot 32767 (als er 2 bytes voor elke sample zijn). Daarnaast heb je een samplerate dat aangeeft hoeveel samples er per seconde aangeeft afgespeeld moet worden om de normale snelheid te bereiken. Met deze informatie kan de computer de originele geluid herconstructueren. + +![Reconstructie geluidbestand](images/8/reconstruction.jpg) + + + +## Geluid programmeren + +We laten twee voorbeelden zien hoe je geluidsdata kan lezen en bewerken. Probeer deze voorbeelden! + +**Voorbeeld 1**: `change_speed` + +De functie `change_speed` zou aanwezig moeten zijn in `wk4ex1.py`, zo niet dan kan je het hier kopiëren en in jouw bestand plakken: + +```python +# De voorbeeldfunctie change_speed +def change_speed(filename, newsr): + """change_speed allows the user to change an audio file's speed. + Arguments: filename, the name of the original file + newsr, the new sampling rate in samples per second + Result: no return value, but + this creates the sound file 'out.wav' + and plays it + """ + print("Het originele geluid afspelen...") + play(filename) + + sound_data = [0, 0] # een "lege" lijst + read_wav(filename, sound_data) # laad gegevens IN sound_data + + samps = sound_data[0] # de samples van de ruwe geluidsgolven + + print("De eerste 10 geluidsdruksamples zijn\n", samps[:10]) + sr = sound_data[1] # de sampling rate, sr + + print("Het aantal samples per seconde is", sr) + + # deze regel is niet echt nodig, maar staat hier voor de consistentie... + newsamps = samps # dezelfde samples als eerder + new_sound_data = [newsamps, newsr] # nieuwe geluidsgegevens + write_wav(new_sound_data, "out.wav") # sla de gegevens op naar out.wav + print("\nNieuw geluid afspelen...") + play("out.wav") # speel het nieuwe bestand 'out.wav' af +``` + +Lees het voorbeeld door en probeer het met de volgende geluidsbestanden: + +```ipython +In: change_speed("swfaith.wav", 44100) # snelle Vader +# ... aantal regels uitvoer ... + +In: change_speed("spam.wav", 11025) # langzame Monty Python +# ... aantal regels uitvoer ... + +In: change_speed("swnotry.wav", 22050) # Yoda op normale snelheid +# ... aantal regels uitvoer ... +``` + +:::{admonition} Hoe werkt deze code? +:class: info + +1. De geluidsdata wordt in *twee* onderdelen teruggegeven door de aanroep naar `read_wav`, via de regels + + ```python + read_wav(filename, sound_data) + samps = sound_data[0] + sr = sound_data[1] + ``` + + Merk op dat dit losse regels zijn en dat `sound_data` begint met de waarde `[0, 0]`. Wat hier gebeurt is dat `read_wav` de lijst `sound_data` zo aanpast dat het element met index `0` de lijst samples bevat en het element met index `1` de sampling rate (`sr`). + +2. Na deze aanroep bevat de variabele `samps` een grote lijst met ruwe luchtdruksamples (floats). **Druk deze lijst niet af**, deze is mogelijk te groot en kan IPython vertragen of zelfs doen crashen! + +3. Bovendien bevat na de aanroep de variabele `sr` een integer waarde met de sampling rate, het aantal samples dat per seconde afgespeeld moet worden om de normale snelheid te bereiken. + +4. Er wordt hier en daar wat afgedrukt met `print` zodat je iets van de gegevens kan zien. + +5. We hebben de nieuwe sampling rate al, dat is het argument `newsr`. Voor de consistentie gebruiken we de variabele `newsamps` om de nieuwe geluidssamples een naam te geven. In dit geval veranderen ze helemaal niet, maar in sommige latere programma's zal `newsamps` verschillen van `samps`. + +6. De code schrijft dan `newsamps` en `newsr` weg naar een bestand `out.wav`, die in de map waar je in bezig bent opgeslagen zal worden. Hierbij wordt een oudere versie van dat bestand overschreven. + +7. Ten slotte speelt de functie het nieuwe bestand af, met de nieuwe sampling rate `newsr`. + +Variaties hierop zul je tegenkomen in alle geluidsfuncties. +::: + +Het volgende voorbeeld laat zien hoe je een nieuw geluid kunt creëren door de samples zelf te veranderen. Dat wil zeggen, `newsamps` zal anders zijn dan `samps` (de oude samples). Vergeet niet dat `samps` een zeer grote lijst van luchtdrukwaarden zal zijn ( ongeveer 50.000 elementen). + +**Voorbeeld 2**: `flipflop` + +De functie `flipflop` zou aanwezig moeten zijn in `wk4ex1.py`, zo niet dan kan je het hier kopiëren en in jouw bestand plakken: + +```python +def flipflop(filename): + """flipflop swaps the halves of an audio file + Argument: filename, the name of the original file + Result: no return value, but + this creates the sound file 'out.wav' + and plays it + """ + print("Het originele geluid afspelen...") + play(filename) + + print("Geluidsgegevens inlezen...") + sound_data = [0, 0] + read_wav(filename, sound_data) + samps = sound_data[0] + sr = sound_data[1] + + print("Nieuw geluid berekenen...") + # dit bepaalt het middelpunt en noemt dat x + x = len(samps) // 2 + newsamps = samps[x:] + samps[:x] + newsr = sr + new_sound_data = [newsamps, newsr] + + print("De nieuwe geluidsgegevens opslaan...") + write_wav(new_sound_data, "out.wav") # schrijf gegevens naar out.wav + + print("Nieuw geluid afspelen...") + play("out.wav") +``` + +Let op het middelste gedeelte van deze code, waar de nieuwe geluidssamples gemaakt worden op basis van de oude. In dit geval is `newsamps` een "geflipflopde" versie van de oude `samps`. Merk verder op dat deze code precies hetzelfde is als de opdracht `flipside` uit [fijne functies](fijne_functies.md): `flipflop` plaatst de tweede helft van het geluid *voor* de eerste helft! + +Bij het schrijven van jouw geluidsbewerkende functies kan je `flipflop` als startpunt gebruiken. + + +### Opdracht 1: + +Probeer eerst onderstaande functie, die al in het bestand `wk4ex1.py` zou moeten staan. Je kan dit uitvoeren met `test()`: + +```python +# a function to make sure everything is working +def test(): + """A test function that plays swfaith.wav + You'll need swfaith.wav in this folder. + """ + play("swfaith.wav") +``` + +Om dit te laten werken, moet jouw Python versie wel geluid ondersteunen (normaal gesproken moet dit geen probleem zijn). Als het niet werkt, is het handig om met iemand samen te werken bij wie het wel werkt. + +Het is ook nodig dat het bestand `swfaith.wav` in dezelfde directory staat als `wk4ex1.py`. Als je in de directory werkt waar je de bestanden hebt uitgepakt, zou dit zo moeten zijn. Als dat niet zo is, dan kan je de bestanden die je hebt uitgepakt kopiëren naar de map waarin je aan het werk bent. + +### Opdracht 2: + +Schrijf nu een geluidsbewerkende functie `reverse` met de volgende signature + +```python +def reverse(filename): + ... +``` + +zodat `reverse` een `filename` accepteert, net als `flipflop`. + +Net als bij `flipflop` moet de sampling rate niet veranderen, maar de functie moet een *omgekeerde* lijst van geluidssamples maken en deze vervolgens op dezelfde manier verwerken als de twee bovenstaande voorbeelden. Dat wil zeggen dat je ze naar het bestand `out.wav` wilt schrijven en vervolgens dat bestand wilt afspelen. + +:::{admonition} Een lijst omdraaien +:class: tip + +Vergeet niet dat je in Python `samps[::-1]` kan schrijven om de lijst `samps` om te draaien! +::: + +Zie het volgende voorbeeld: + +```ipython +In: reverse("swfaith.wav") # redaV htraD klinkt spookachtiger maar minder intimiderend +# ... veel uitvoer ... +``` + +Merk op dat deze functie `reverse` *geen* gebruik hoeft te maken van de functies die je hiervoor geschreven hebt, maar de volgende functies wel! + + +### Opdracht 3: `volume` + +Schrijf nu een geluidsbewerkende functie `volume` met de volgende signature + +```python +def volume(filename, scale_factor): + ... +``` + +zodat `volume` een `filename` accepteert zoals gebruikelijk en een floating-point waarde `scale_factor`. Vervolgens moet `volume` op de gebruikelijke manier het geluid verwerken, waarbij het uitvoerbestand en het afgespeelde geluid de amplitude (volume) wordt geschaald met de schaalfactor `scale_factor`. Met andere woorden, elke sample moet worden vermenigvuldigd met `scale_factor`. + +:::{admonition} Gebruik de hulpfunctie `scale` +:class: tip + +Gebruik de hulpfunctie `scale` die je eerder hebt geschreven. Wat je nodig hebt is + +```python +newsamps = scale(..., ...) +``` + +Dit is een typisch voorbeeld, steeds zullen maar kleine aanpassingen nodig zijn voor elke volgende audio functie die je gaat schrijven. +::: + +Zie de volgende voorbeelden: + +```ipython +In [1]: volume("swfaith.wav", .1) # Een rustiger Darth... +# ... veel uitvoer ... + +In [2]: volume("swfaith.wav", 10.0) # Een hele drukke Darth! +# ... veel uitvoer ... +``` + +Je zult merken dat jouw gehoor zich opmerkelijk goed aanpast aan de veranderingen in het absolute volume van deze functie, waardoor het waargenomen effect aanzienlijk minder is dan je zou verwachten. + +Je zult ook merken dat als je het volume te veel verhoogt, het geluid vervormd raakt, net als wanneer een versterker op 11 wordt gezet. + +### Opdracht 4: `static` + +Schrijf nu een geluidsbewerkende functie `static` met de volgende signature + +```python +def static(filename, probability_of_static): + ... +``` + +zodat `static` een `filename` (zoals gebruikelijk) en een floating-point waarde `probability_of_static` accepteert, waarvan je mag aannemen dat deze tussen 0 en 1 ligt. + +`static` moet het geluid op de gebruikelijke manier verwerken, waarbij de uitvoersamples met een kans gelijk aan `probability_of_static` moeten worden vervangen door een willekeurig getal tussen -32768 en 32767. + +Je gebruikt hier de hulpfunctie `replace_some` die je al eerder hebt geschreven. Je hoeft `randomize` niet te gebruiken want deze wordt al door `replace_some` gebruikt! + +Zie de volgende voorbeelden: + +```ipython +In [1]: static("swfaith.wav", .05) # Vader, die een tunnel in rijdt +# ... veel uitvoer ... + +In [2]: static("swfaith.wav", .25) # Vader op een dial-upverbinding vanuit een galaxy far, far away +# ... veel uitvoer ... +``` + +Je zou kunnen uitproberen hoe hoog je het percentage ruis kunt verhogen totdat het origineel niet meer herkenbaar is. Mensen kunnen zich hier minder goed aan aanpassen dan aan volumeveranderingen. + +### Opdracht 5: `overlay` + +Schrijf nu een geluidsbewerkende functie `overlay` met de volgende signature + +```python +def overlay(filename1, filename2): + ... +``` + +zodat `overlay` twee bestandsnamen accepteert, en een nieuw geluid creëert dat de twee overlapt (combineert). Het resultaat moet net zo lang zijn als de kortere van de twee. (negeer eventuele extra samples, net als in `add_scale_2`.) + +Gebruik de `add_scale_2` hulpfunctie om je hierbij te helpen! Op die manier kan je het relatieve volume van de twee bestanden aanpassen. Het is ook mogelijk, maar zeker niet verplicht, om meer argumenten toe te voegen aan de functie `overlay`, zodat je hiermee de relatieve volumes kan aanpassen (of samples kan bijsnijden, maar dat is lastiger). + +*Onthoud* dat `add_scale_2(samps1, samps2, 0.5, 0.5)` lijsten (`samps`) als argumenten accepteert en geen bestandsnamen, dat zijn gewoon strings! De `samps` zijn lijsten van de ruwe geluidsdata. + +Zie het volgende voorbeeld: + +```ipython +In: overlay("swfaith.wav", "swnotry.wav") # Vader vs. Yoda +# ... veel uitvoer ... +``` + +Extra: hoe kun je `overlay` zo aanpassen dat deze het langere geluid niet afkapt? In plaats van het af te kappen, zou je het kunnen laten doorgaan tegen stilte, of je zou het kortere geluid kunnen herhalen? + +De volgende functie combineert een bestand met een verschoven versie van zichzelf. + +### Opdracht 6: `echo` + +Probeer een geluidsbewerkende functie `echo` te schrijven met de volgende signature + +```python +def echo(filename, time_delay): +``` + +zodat `echo` een bestandsnaam accepteert zoals gebruikelijk en een floating-point waarde `time_delay`, die een aantal seconden voostelt. + +Dan moet `echo` het geluid op de gebruikelijke manier verwerken, waarbij het originele geluid wordt overlapt door een kopie van zichzelf die in de tijd wordt verschoven met `time_delay` seconden. + +Om de geluiden te laten overlappen zal je `add_scale_2` weer willen gebruiken, zoals eerder. + +Om de geluiden te verschuiven kan je de sampling rate gebruiken om het aantal samples voor een bepaald aantal seconden te bepalen: + +- Als bijvoorbeeld `time_delay` een waarde `0.1` heeft en de sampling rate `22050`, dan moet je `2205` samples wachten + +- Of, als `time_delay` een waarde `0.25` heeft en de sampling rate `44100`, dan is het aantal samples dat je moet wachten `11025` + +:::{admonition} Stilte toevoegen +:class: tip + +De meest gemakkelijke manier om een "wachttijd" aan samples toe te voegen is om een "lege ruimte" of "leeg geluid" aan het begin +van `samps` toe te voegen, en dit kan je doen door een lijst nullen aan het begin van `samps` te plaatsen! Bijvoorbeeld, + +```python +samps2 = [0] * 42 + samps +``` + +zal 42 samples "wachten" door 42 samples *zonder* geluid aan het begin van de geluidsdata `samps` toe te voegen. + +Je hebt waarschijnlijk een andere waarde dan 42 nodig, de uitdaging is om daar de juiste waarde te berekenen! Hoe zou je er nu achter kunnen komen welke integer waarde je nodig hebt *in plaats van* 42? + +- Bedenk dat je weet hoe lang je wilt wachten (in seconden) en wat de sampling rate is (in samples per seconde). + +- Zorg ervoor dat je een integer gebruikt. Bendenk dat als je een floating-pointg getal `f` hebt, je daar een integer van kan maken met `int(f)`. + +Terzijde, dit is maar één mogelijke aanpak, er zijn ook andere benaderingen mogelijk voor `echo`. +::: + +Zie het volgende voorbeeld: + +```ipython +In [1]: echo("swfaith.wav", .1) # Hoe veel nullen zijn er aan het begin nodig? +# ... veel uitvoer ... +``` + + + +### Opdracht 7: `gen_pure_tone` + +De laatste voorbeeldfuncties genereren een zuivere sinusvormige toon. Hier is de code, hoewel deze ook in het bestand zou moeten staan: + +```python +def gen_pure_tone(freq, seconds, sound_data): + """pure_tone returns the y-values of a cosine wave + whose frequency is freq Hertz. + It returns nsamples values, taken once every 1/44100 of a second. + Thus, the sampling rate is 44100 hertz. + 0.5 second (22050 samples) is probably enough. + """ + if sound_data != [0, 0]: + print("De waarde van sound_data moet [0, 0] zijn.") + return + sampling_rate = 22050 + # hoeveel samples we moeten genereren + nsamples = int(seconds * sampling_rate) # naar beneden afgerond + # de factor f om de frequentie te schalen + f = 2 * math.pi / sampling_rate # omrekenen van samples naar Hz + # de factor a om de amplitude te schalen + a = 32767.0 + sound_data[0] = [a * math.sin(f * n * freq) for n in range(nsamples)] + sound_data[1] = sampling_rate + return sound_data + + +def pure_tone(freq, time_in_seconds): + """Generates and plays a pure tone of the given frequence.""" + print("Toon genereren...") + sound_data = [0, 0] + gen_pure_tone(freq, time_in_seconds, sound_data) + + print("De nieuwe geluidsgegevens opslaan...") + write_wav(new_sound_data, "out.wav") # schrijf gegevens naar out.wav + + print("Nieuw geluid afspelen...") + play("out.wav") +``` + + + +**a** Bekijk deze code en probeer het uit om een gevoel te krijgen voor wat het doet, hoewel de wiskunde achter de sinusgolf niet cruciaal is. + +De belangrijke details zijn dat de functie `pure_tone` een gewenste frequentie `freq` en een tijdsduur `time_in_seconds` accepteert. De wiskundige details worden dan overgelaten aan `gen_pure_toon`. + +Zie het volgende voorbeeld: + +```ipython +In [1]: pure_tone(440, 0.5) # een A van 0,5 seconde in concertstemming +# ... veel uitvoer ... +``` + +Je kan de frequenties van andere noten vinden op [Wikipedia](https://nl.wikipedia.org/wiki/Toonhoogtetabel) en op vele andere plekken. Hier is ook een klein overzicht: + +![Frequentietabel](images/notes.png) + +Het is interessant om op te merken dat C0 onder het bereik van het normale menselijke gehoor ligt (we kunnen slechts tot ongeveer 20 Hz horen), maar B8 laat veel ruimte over (de meeste mensen onder de 40 jaar kunnen tot 20.000 Hz of hoger horen). Ook gaan de meeste piano's alleen naar A0 (28 Hz), maar de Bösendorfer Imperial Concert Grand heeft extra toetsen (zwart gekleurd) die helemaal naar C0 gaan. Voor het geval je extra bas nodig hebt! + + + +**b** Audiofunctie 6: `chord` + +Het laatste probleem is om op basis van het bovenstaande voorbeeld een functie te schrijven die akkoorden maakt, met de volgende handtekening: + +```python +def chord(f1, f2, f3, time_in_seconds): + ... +``` + +zodat `chord` drie floating-point frequenties `f1`, `f2` en `f3` accepteert, en een floating-point getal `time_in_seconds` voor de tijdsduur. + +De functie `chord` moet een akkoord van 3 noten maken en spelen gebaseerd op deze frequenties. + + +Je zult drie *sets* van `samps` en `sr` uit `gen_pure_tone` willen krijgen, bijvoorbeeld, + +```python +samps1, sr1 = gen_pure_tone(f1, time_in_seconds, [0, 0]) +samps2, sr2 = gen_pure_tone(f2, time_in_seconds, [0, 0]) +samps3, sr3 = gen_pure_tone(f3, time_in_seconds, [0, 0]) +``` + +Vervolgens heb je echt een `add_scale_3` functie nodig, hoewel we die nog niet hebben. Maar je kunt deze wel maken! (Je zou `add_scale_2` en `add_3` als uitgangspunt kunnen nemen, maar we raden aan om `add_scale_3` als zelfstandige functie te schrijven, en niet om die andere functies aan te roepen). + +Tot slot moet je de resulterende lijst samples nemen (noem deze bijvoorbeeld `newsamps`) en deze verwerken met behulp van code die je van de vorige functies hebt geleend: + +```python +new_sound_data = [newsamps, newsr] + +print("De nieuwe geluidsgegevens opslaan...") +write_wav(new_sound_data, "out.wav") # schrijf gegevens naar out.wav + +print("Nieuw geluid afspelen...") +play("out.wav") +``` + +Zie het volgende voorbeeld: + +```ipython +In [1]: chord(440.000, 523.251, 659.255, 1.0) # A mineur +# ... veel uitvoer ... +``` + +Klinkt jouw akkoord vreselijk? Weet je nog wat er gebeurde toen je het volume te veel verhoogde? `gen_pure_tone` produceert een toon die op maximaal volume staat. Wanneer je twee (of drie) van dergelijke tonen combineert, tellen hun piekvolumes bij elkaar op en het resultaat is te luid voor de computer, waardoor er vervorming ontstaat. Bedenk wat je zou +kunnen doen om hiervoor te compenseren zonder het akkoord *te stil* te maken. In andere woorden: + +- Je zult de totale amplitude op `1.0` willen houden. + +- Aangezien de amplitude van elk origineel `1.0` is, moet je een breuk (fractie) als schaalfactor gebruiken om ervoor te zorgen dat de totale amplitude van de opgetelde golven op 1.0 of minder blijft. + +- Als de amplitude van golf groter is dan `1.0`, zal hij "afgekapt" worden door de luidsprekers. Dit klinkt alsof er luide ruis in het geluid zit (of het klinkt gewoon heel slecht). + + +Uitdaging: Gebruik de bovenstaande frequentietabel om het akkoord te veranderen van een A-mineur naar een A-majoor akkoord. Of maak jouw eigen akkoord... + +Maar hoe zit het met het creëren van een *C mineur septiem* (of overmatig) akkoord? + +Inderdaad, je zou grotere akkoorden kunnen maken met willekeurig veel tonen... of andere ongewone/onbelangrijke/inspirerende/verstorende algoritmisch gegenereerde geluidseffecten. We moedigen je zeker aan om dingen uit te proberen! Het is niet vreselijk moeilijk om iets te schrijven dat echte muziek speelt! + +:::{admonition} Verder uitbreiden +:class: tip + +Het kan helpen om functies als `a_flat(duration)` of `cm7(duration)` te hebben, en dan iets te schrijven dat daarop voortbouwt. +::: + +## Inleveren + +Je hoeft voor dit practicum alleen maar het bestand `wk4ex1.py` in te leveren, niet de andere bestanden die je gedownload hebt. \ No newline at end of file diff --git a/practicals/9_lusjes.ipynb b/practicals/9_lusjes.ipynb deleted file mode 100644 index 7e69938b..00000000 --- a/practicals/9_lusjes.ipynb +++ /dev/null @@ -1,188 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Lang leven lusjes" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## Meer `for`" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let op de plaatsing van de return statement. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "def fun1B():\n", - " for i in range(1, 6):\n", - " if i % 2 == 0:\n", - " print(\"i is\", i)\n", - " return" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "i is 2\n" - ] - } - ], - "source": [ - "fun1B()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "def fun2B():\n", - " for i in range(1, 6):\n", - " if i % 2 == 0:\n", - " print(\"i is\", i)\n", - " return" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "fun2B()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "subslide" - } - }, - "outputs": [], - "source": [ - "def fun3B():\n", - " for i in range(1,6):\n", - " if i % 2 == 0:\n", - " print(\"i is\", i)\n", - " return" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "i is 2\n", - "i is 4\n" - ] - } - ], - "source": [ - "fun3B()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "source": [ - "```python\n", - "def fun3B():\n", - " for i in range(1,6):\n", - " if i % 2 == 0:\n", - " print(\"i is\", i)\n", - "return\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "source": [ - "![SyntaxError return](images/9/syntax_error_return.png)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/practicals/9a_lusjes.md b/practicals/9a_lusjes.md new file mode 100644 index 00000000..6bed2541 --- /dev/null +++ b/practicals/9a_lusjes.md @@ -0,0 +1,197 @@ +# lang leven lusjes + +## Opdracht 1 +Wat is de output van onderstaande programma's? +a. +```python +def fun1B(): + for i in range(1, 6): + if i % 2 == 0: + print("i is", i) + return +fun1B() +``` + +b. +```python +def fun2B(): + for i in range(1, 6): + if i % 2 == 0: + print("i is", i) + return +fun2B() +``` + +c. +```python + def fun3B(): + for i in range(1,6): + if i % 2 == 0: + print("i is", i) + return +fun3B() +``` + +d. +```python +def fun4B(): + for i in range(1,6): + if i % 2 == 0: + print("i is", i) +return +fun4B() +``` + +## Opdracht 2 +Iets heel vaak herhalen is waar computers op hun best zijn; en mensen in het algemeen duidelijk niet van gediend zijn! + +**a.** Plak, om een beeld te krijgen van het gebruik van *lussen*, deze functie met commentaar in een nieuw bestand `wk9wc2.py`. Dit is een functie die de faculteit berekent door middel van de `for`-lus van Python. + +```python +# +# wk9wc2.py - Aan de slag met lussen! +# +# Naam: +# +def main(): + """main functie""" + +def testing(): + """test functie""" + assert fac(0) == 1 + assert fac(5) == 120 + +def fac(n): + """Loop-based factorial function + + Argument: a nonnegative integer, n + Return value: the factorial of n + """ + result = 1 # beginwaarde; lijkt op een basisgeval + for x in range(1, n + 1): # herhaal van 1 tot en met n + result = result * x # pas het resultaat aan door keer x te doen + return result # merk op dat dit NA de lus is! + +testing() +main() + +``` + +**b** Lusfunctie 1: `power(b, p)` + +Begin met het lezen en uitvoeren van bovenstaand bestand. Je zult zien dat de tests slagen. + +Maak daarna een nieuwe functie `power(b, p)`, door de bovenstaande functie te kopiëren en aan te passen, of door de fuctie zelf te schrijven op dezelfde manier. Deze functie + +- Accepteert een numerieke waarde `b`, het grondtal +- Accepteert een niet-negatieve integer `p`, de macht (`p` kan 0 zijn) +- Geeft de waarde van `b ** p` terug +- De functie moet een `for`-lus gebruiken! Je mag niet gewoon `b ** p` gebruiken... +- In dit geval doen we alsof `power(0, 0)` gelijk is aan `1.0`, ook als dat wiskundig niet helemaal juist is. + +Hier zijn een paar tests die je kan omzetten naar asserts: + +```python +# +# Tests voor de lus-versie van machtsverheffen +# +print("power(2, 5): is 32 ==", power(2, 5)) +print("power(5, 2): is 25 ==", power(5, 2)) +print("power(42, 0): is 1 ==", power(42, 0)) +print("power(0, 42): is 0 ==", power(0, 42)) +print("power(0, 0): is 1 ==", power(0, 0)) +``` + +::: {admonition} Overeenkomst met de faculteitsfunctie +:class: tip + +De parameter `n` uit de faculteitsversie heeft dezelfde functie als de parameter `p` in deze functie. Het grondtal `b` wordt alleen gebruikt om mee te vermenigvuldigen. +::: + + +## Opdracht 3 + +**a** Wat is de output van onderstaande programma? + +```python +def function(L): + result = 0 + for e in L: + result = result + e # of result += e + return result + +print(function([4, 5, 6])) +``` + +**b** Lusfunctie 2: `summed_odds(L)` + +Maak nu zelf een functie `summed_odds(L)` door deze functie als voorbeeld te gebruiken, die + +- Een lijst integers `L` accepteert. +- Je mag ervan uitgaan dat de lijst alleen integers bevat. +- De functie moet de som teruggeven van alle **oneven** getallen in `L`. +- Maak gebruik van een lus! + +Vergeet niet de functie voldoende te testen! + +```python +# tests! +assert summed_odds([4, 5, 6]) == 5 +assert summed_odds(range(3, 10)) == 24 +``` + +!!! tip "Een `if`-statement" + Maak *binnen* de lus gebruik van een `if`-statement, zodat je de waarde alleen aan het resultaat toevoegd onder de juiste omstandigheden. + + + +## opracht 4 + +**a** Wat is de output van onderstaande programma? + +```python +def function(L): + result = [] + for e in L: + if e < 5: + result += [e] + return result + +print(function([1, 2, 3, 4, 5, 6])) +``` + +**b** Lusfunctie 3: 'factors(n)' + +Maak nu zelf een functie 'factors(n)' die: + +- een integer 'n' ontvangt +- een lijst teruggeeft met alle factoren van 'n' + +Vergeet niet de functie voldoende te testen +```python +assert factors(12) == [1, 2, 3, 4, 6, 12] +assert factors(15) == [1, 3, 5, 15] +``` + + +## Opdracht 5 + +**a** Wat is de output van onderstaande programma? + +```python +def function(s): + result = '' + for c in s: + result = c + result + return result + +print(function("Hanze")) +``` + +**b** Lusfunctie 4: 'count_vowels(s)' + +Maak zelf een functie 'count_vowels(s) die: +- Een string 's' accepteert +- de functie telt alle klinkers in de string en geeft dat terug als integer. + +Maak zelf een aantal assertions om je functie te testen. \ No newline at end of file diff --git a/practicals/9b_tr_investeringen.md b/practicals/9b_tr_investeringen.md new file mode 100644 index 00000000..147bab84 --- /dev/null +++ b/practicals/9b_tr_investeringen.md @@ -0,0 +1,155 @@ +# Statistieken + +## Net formatteren... + +Dit is optioneel, maar als je net geformateerde kolommen wilt afdrukken kan de volgende aanpak helpen. De formatteerstring geeft aan *hoe* dingen geformateerd worden, en de invoerwaardes voor `.format` geven aan *welke* waardes op die manier geformatteerd zullen worden. + +```ipython +In: print("{0: >4} : € {1: >6}".format("Dag", "Prijs")) + Dag : € Prijs + +In: print("{0: >4} : € {1: >6}".format(3, 42)) + 3 : € 42 + +In: print("{0: >4} : € {1: >6}".format(11, 27042)) + 11 : € 27042 +``` + +De *formatteerstring*, te weten `"{0: >4} : $ {1: >6}"`, zegt in het kort drie dingen: + +* Druk het te formatteren argument #`0` rechtsuitgelijnd af in een ruimte met een breedte van 4 +* Druk daarna een spatie, een dubbele punt, een spatie, een euroteken en een spatie af (allemaal precies zoals opgegeven) +* Druk daarna het te formatteren argument #`1` rechtsuitgelijnd af in een ruimte met een breedte van 6. + +Merk op dat het `.format(input0, input1)` na de formatteerstring de te formatteren waardes opgeeft! + +Je kan hiermee experimenteren om het resultaat te krijgen wat je wilt. Afdrukken zonder je druk te maken over het formaat is ook goed! + + +## Opdracht 1 + +a. Kopieer onderstaande code over naar een bestand genaamd 'wk3wc2.py' +```python +# +# Programma voor een lus voor gebruikersinteractie +# + +def main(): + """The main user-interaction loop""" + secret_value = 4.2 + + L = [30, 10, 20] # een beginlijst + + while True: # de lus voor gebruikersinteractie + print("\n\nDe lijst is", L) + menu() + choice = input("Maak een keuze: ") + + # + # De invoer van de gebruiker "opschonen en controleren" + # + try: + choice = int(choice) # omzetten naar een int! + except: + print("Ik begreep de invoer niet! Verder gaan...") + continue + + # de gekozen menu-optie uitvoeren + # + if choice == 9: # We willen stoppen + break # De hele while-lus afbreken + + elif choice == 0: # We willen doorgaan... + continue # Terug naar het begin van de while-lus + + elif choice == 1: # We willen een nieuwe lijst invoeren + new_L = input("Voer een nieuwe lijst in: ") # _iets_ invoeren + + # + # De invoer van de gebruiker "opschonen en controleren" + # + try: + new_L = eval(new_L) # eval voert de Python-interpreter uit! Let op: Gevaarlijk! + if not isinstance(new_L, list): + print("Dat lijkt geen lijst. L wordt niet aangepast.") + else: + L = new_L # Hier is het wel OK, dus we passen onze lijst L aan + except: + print("Ik begreep de invoer niet. L wordt niet aangepast.") + elif choice == 2: + min_val = find_min(L) + print("De kleinste waarde van L is", min_val) + + else: + print(choice, " ? Dat staat niet op het menu!") + print() + print("Tot gisteren!") + + +def menu(): + """A function that simply prints the menu""" + print() + print("(0) Doorgaan!") + print("(1) Nieuwe lijst invoeren") + print("(2) Kleinste waarde vinden") + print("(9) Stoppen! (einde)") + print() + + +def find_min(L): + """find min uses a loop to return the minimum of L. + Argument L: a nonempty list of numbers. + Return value: the smallest value in L. + """ + result = L[0] + for x in L: + if x < result: + result = x + return result + +main() +``` + +b. Test of de code werkt. Probeer een nieuwe lijst in te voeren en hiervan het minimum te bepalen. +c. Schrijf de functie `print_lijst(L)` dat een lijst accepteerd en uitprint als een tabel, met de index in de eerste kolom en de waarden in de tweede. + +```ipython +index Waarde +----- ------ +0 20 +1 10 +2 30 +``` + +d. Pas het programma aan zodat er een optie is om de lijst te printen. + + + +## Opdracht 2 +a. Schrijf de functie `find_max(L)` dat een lijst accepteerd en de hoogste waarde van die lijst teruggeeft. Je mag niet de ingebouwde `max()` functie gebruiken. +b. Pas het programma zo aan dat het vinden van de grootste waarde een optie is. + +## Opdracht 3 +a. Schrijf de functie `summing(L)` dat een lijst accepteerd en de som van alle waardes teruggeeft. Je mag niet de ingebouwde functie `sum()` gebruiken. Vergeet niet je functie te testen. +b. Schrijf de functie `size(L)` dat een lijst accepteerd en de lengte van de lijst teruggeeft. Je mag niet de ingebouwde functie `len()` gebruiken. Vergeet niet je functie te testen. +c. Schrijf de functie `gemiddelde(L)` dat een lijst accepteerd en de gemiddelde waarde van die lijst teruggeeft. Vergeet niet je functie te testen. Maak gebruik van de functies `summing()` en `size()` +d. Pas het programma zo aan dat er een optie is om de gemiddelde waarde van de lijst te printen. + + +## Opdracht 4: Counting sort. +Counting sort, soms ook count-sort genoemd, is een sorteeralgoritme, dat alleen kan worden gebruikt voor gehele getallen. Als de kleinste en grootste waarden niet bekend zijn, zullen deze vooraf aan het sorteren bepaald moeten worden. Daarna wordt er bijgehouden hoevaak een waarde in de lijst voorkomt, zogenaamd turven. De kleinste waarde staat op index 0. De lijst is evenlang als dat er getallen zijn tussen het minimum en maximum waarde. + +Een voorbeeld: De ongesorteerde reeks gehele getallen is 6, 4, 6, 8, 9, 6, 4, 9, 5, 5, 5, 8, 5. Het kleinste getal is 4 en het grootste is 9. Op basis daarvan wordt van elk element tussen 4 en 9 geteld hoe vaak het in de rij voorkomt. Geturfd resultaat: +```ipython +index count +0 (4) 2 +1 (5) 4 +2 (6) 3 +3 (7) 0 +4 (8) 2 +5 (9) 2 +``` +De resultaten van de telling worden weer achter elkaar geplaatst en het sorteren is voltooid: 4, 4, 5, 5, 5, 5, 6, 6, 6, 8, 8, 9, 9. + +a. Schrijf de functie `counting sort()` dat een lijst ontvangt en een gesoorteerde lijst teruggeeft. Maakt gebruik van counting sort. +b. Pas het programma zo aan dat er de optie is om de lijst te sorteren. \ No newline at end of file diff --git a/practicals/images/8/Amplitude_freq.PNG b/practicals/images/8/Amplitude_freq.PNG new file mode 100644 index 00000000..3c296ffc Binary files /dev/null and b/practicals/images/8/Amplitude_freq.PNG differ diff --git a/practicals/images/8/Sampling.png b/practicals/images/8/Sampling.png new file mode 100644 index 00000000..f74144a5 Binary files /dev/null and b/practicals/images/8/Sampling.png differ diff --git a/practicals/images/8/reconstruction.jpg b/practicals/images/8/reconstruction.jpg new file mode 100644 index 00000000..ffdc5cf4 Binary files /dev/null and b/practicals/images/8/reconstruction.jpg differ diff --git a/practicals/images/8/wave.png b/practicals/images/8/wave.png new file mode 100644 index 00000000..98484f73 Binary files /dev/null and b/practicals/images/8/wave.png differ diff --git a/practicals/images/audacitySS.png b/practicals/images/audacitySS.png new file mode 100644 index 00000000..a492ed1c Binary files /dev/null and b/practicals/images/audacitySS.png differ diff --git a/practicals/images/notes.png b/practicals/images/notes.png new file mode 100644 index 00000000..9fb6379b Binary files /dev/null and b/practicals/images/notes.png differ diff --git a/problems/basis/4_midterm.md b/problems/basis/4_midterm.md index c9bdad6c..1d8a1b9b 100644 --- a/problems/basis/4_midterm.md +++ b/problems/basis/4_midterm.md @@ -74,10 +74,10 @@ else: ``` - - **a** ‘Groep 1’ - - **b** ‘Groep 2’ - - **c** ‘Groep 3’ - - **d** ‘Groep 4’ + - **a** ‘groep 1’ + - **b** ‘groep 2’ + - **c** ‘groep 3’ + - **d** ‘groep 4’ ## 5. Wat print dit programma? @@ -283,11 +283,11 @@ function("Hanze", 4) ``` - **a** H, 0, a, 1, n, 2, z, 3, e, 4 - - **b** H, a, n, z, e, 4, 3, 2, 1, 0 - - **c**, e, z, n, a, H, 4, 3, 2, 1, 0 - - **d** e, z, n, a, H, 4, 3, 2, 1 - - **e**. H, a, n, z, e, 4, 3, 2, 1 - - **f**. e, 4, z, 3, n, 2, a, 1, H + - **b** H, a, n, z, e, 0, 1, 2, 3, 4, + - **c** e, z, n, a, H, 4, 3, 2, 1, 0 + - **d** e, z, n, a, H, 1, 2, 3, 4 + - **e** H, a, n, z, e, 4, 3, 2, 1 + - **f** e, 4, z, 3, n, 2, a, 1, H ## 16. Wat print dit programma? @@ -352,9 +352,9 @@ In welke volgorde moeten deze regels staan om de volgende output te krijgen: - **a** 1, 2, 5, 4, 6, 3 - - **b** 2, 4, 5, 6, 3, 1 + - **b** 2, 4, 6, 5, 3, 1 - **c** 1, 2, 4, 6, 5, 3 - - **d** 2, 5, 4, 3, 6, 1 + - **d** 2, 5, 4, 6, 3, 1 ## 19. Gegeven de volgende regels aan code. diff --git a/problems/basis/8_list_comprehension.ipynb b/problems/basis/8_list_comprehension.ipynb new file mode 100644 index 00000000..56899c4f --- /dev/null +++ b/problems/basis/8_list_comprehension.ipynb @@ -0,0 +1,404 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# List comprehension\n", + "\n", + "Gebruik list comprehension om de volgende problemen op te lossen." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Opgaven" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `mult_of_five`\n", + "\n", + "De functie `mult_of_five` accepteert een integer `n` en heeft als resultaat een list met de veelvouden 1 tot en met `n` van 5. Gebruik hier de ingebouwde functie `range`.\n", + "\n", + "De functie `range` begint met 0, je zult voor 1 als volgt moeten corrigeren:\n", + "\n", + "```python\n", + "range(1, n + 1)\n", + "```\n", + "\n", + "Zie verder ook de [documentatie](https://docs.python.org/3/library/stdtypes.html#range) voor `range`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "def mult_of_five(n):\n", + " \"\"\"Return a list containing the first n multiples of 5\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jow oplossingen te testen:\n", + "\n", + "```python\n", + "assert mult_of_five(0) == []\n", + "assert mult_of_five(1) == [5]\n", + "assert mult_of_five(2) == [5, 10]\n", + "assert mult_of_five(3) == [5, 10, 15] \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `divisible_by`\n", + "\n", + "De functie `divisible_by` accepteert een integer `n` en een list `L` en geeft als resultaat een list terug met alle waarden in `L` deelbaar door `n`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def divisible_by(n, L):\n", + " \"\"\"Return a list with values in L divisible by n\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert divisible_by(5, [15, 0, 23, 4]) == [15, 0]\n", + "assert divisible_by(3, [2, 4, 8, 10]) == []\n", + "assert divisible_by(2, []) == []\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `starts_with`\n", + "\n", + "De functie `starts_with` accepteer een string `s` en een list met string `L` en heeft als resultaat een list met alle waarden in `L` die beginnen met `s`.\n", + "\n", + "- je mag de ingebouwde string methode `startswith` **niet** gebruiken\n", + "- je mag **geen** recursie gebruiken\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def starts_with(s, L):\n", + " \"\"\"Return all strings in L which start with s\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert starts_with(\"a\", []) == []\n", + "assert starts_with(\"a\", [\"bbc\", \"brits\", \"omroep\"]) == []\n", + "assert starts_with(\"a\", [\"abc\", \"cde\", \"aha\", \"abba\"]) == [\"abc\", \"aha\", \"abba\"]\n", + "assert starts_with(\"ab\", [\"abc\", \"cde\", \"aha\", \"abba\"]) == [\"abc\", \"abba\"]\n", + "assert starts_with(\"abc\", [\"abc\", \"cde\", \"aha\", \"abba\"]) == [\"abc\"]\n", + "assert starts_with(\"abcd\", [\"abc\", \"cde\", \"aha\", \"abba\"]) == []\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `double_letters`\n", + "\n", + "De functie `double_letters` accepteert een list met strings `L` en heeft als resultaat een list met alle waarden verdubbeld." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def double_letters(L):\n", + " \"\"\"Double all letters in L\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert double_letters([\"a\"]) == [\"aa\"]\n", + "assert double_letters([\"a\", \"1\", \"23\"]) == [\"aa\", \"11\", \"2323\"]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `num_as`\n", + "\n", + "De functie `num_as` accepteert een list met met strings `L` en heeft als resultaat een integer het aantal keer dat het karakter \"a\" zich in `L` bevindt." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def num_as(L):\n", + " \"\"\"Count the number of a's in L\n", + " \"\"\"\n", + " return sum([...])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert num_as([\"a\", \"b\", \"c\", \"a\", \"d\"]) == 2\n", + "assert num_as([\"y\", \"b\", \"c\", \"x\", \"d\"]) == 0\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `vwl`\n", + "\n", + "De functie `vwl` accepteert een string `s` en heeft als resultaat het aantal klinkers in `s`. Klinkers zijn \"a\", \"e\", \"i\", \"o\" en \"u\".\n", + "\n", + "- gebruik hier de ingebouwde functie `sum` in combinatie met list comprehension\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def vwl(s):\n", + " ''' Return the number of vowels in a string\n", + " '''\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert vwl(\"appel\") == 2\n", + "assert vwl(\"bbc\") == 0\n", + "assert vwl(\"oma\") == 2\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `add_tax`\n", + "\n", + "De functie `add_tax` accepteert een getal (float of integer) `t` groter dan 0 en een lijst met getallen `L`. `t` staat voor een percentage, `L` bevat prijzen van artikelen en het resultaat is een lijst met prijzen verhoogd met percentage `t`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def add_tax(t, L):\n", + " \"\"\"Increment each item in L with t percent tax\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert add_tax(7, [10, 100, 30, 40]) == [10.7, 107.0, 32.1, 42.8]\n", + "assert add_tax(7, [0, 16, 8]) == [0.0, 17.12, 8.56]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `above_below_freeze`\n", + "\n", + "De functie `above_below_freeze` accepteert een lijst met getallen (integer of float) `L` die staan voor temperaturen en heeft als resultaat een list waar voor elke waarde \"onder\", \"boven\" of \"gelijk\" aan het vriespunt (0 graden) is ingevuld.\n", + "\n", + "- je mag hier een hulpfunctie schrijven, indien nodig\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def above_below_freeze(L):\n", + " \"\"\"Return whether each item in L is below, above or \n", + " equals freezing temperature as a string representation\n", + " \"\"\"\n", + " ...\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert above_below_freeze([-1, 0, 10]) == [\"onder\", \"gelijk\", \"boven\"]\n", + "assert above_below_freeze([21, -21, 0]) == [\"boven\", \"onder\", \"gelijk\"]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `zipper`\n", + "\n", + "De functie `zipper` accepteert twee lists `L1` en `L2` van gelijke lengte. Het resultaat is een *list of lists* waar elk element de paarsgewijze combinatie (\"ritsen\") van waarden in `L1` en `L2` is." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def zipper(L1, L2):\n", + " \"\"\"Pairwise combine lists L1 and L2\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert zipper([1, 3, 5], [2, 4, 6]) == [[1, 2], [3, 4], [5, 6]]\n", + "assert zipper([10, 9, 12], [\"jan\", \"feb\", \"mar\"]) == [[10, \"jan\"], [9, \"feb\"], [12, \"mar\"]]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `only_evens`\n", + "\n", + "De functie `only_evens` accepteert een list met integers `L` en geeft als resultaat een list met de even getallen in `L`.\n", + "\n", + "- gebruik de hulpfunctie `is_even` in de list comprehension.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def is_even(x):\n", + " \"\"\"Return True if x is even, False otherwise\n", + " \"\"\"\n", + " return x % 2 == 0\n", + "\n", + "def only_evens(L):\n", + " \"\"\"Returns a list containing the even numbers in L\n", + " \"\"\"\n", + " return [...]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gebruik de volgende assertions om jouw oplossing te testen:\n", + "\n", + "```python\n", + "assert only_evens([1, 1, 1]) == []\n", + "assert only_evens([2, 2, 2]) == [2, 2, 2]\n", + "assert only_evens([0, 1, 2, 3]) == [0, 2]\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/problems/basis/9_python_bat.md b/problems/basis/9_python_bat.md new file mode 100644 index 00000000..19169cef --- /dev/null +++ b/problems/basis/9_python_bat.md @@ -0,0 +1,43 @@ +# Lussen in PythonBat! + +In deze opgave ga je oefenen met de lussen van Python: `for` en `while`. + +Er staan 12 lusproblemen op de twee PythonBat-pagina's: + +* Zes "medium list problems" om lussen te gebruiken op [CodingBat](http://codingbat.com/python/List-2) +* Zes "medium python string problems" om lussen te gebruiken op [CodingBat](http://codingbat.com/python/String-2) + +Als je bijvoorbeeld de pagina met ["medium python string problems"](http://codingbat.com/python/String-2) opent, zie je dat het eerste probleem `double_char` heet. De pagina zegt + +> Given a string, return a string where for every char in the original, there are two chars. +> +> ``` +> double_char('The') → 'TThhee' +> double_char('AAbb') → 'AAAAbbbb' +> double_char('Hi-There') → 'HHii--TThheerree' +> ``` + +Het leuke aan de PythonBat-pagina's is dat ze je code meteen controleren. + +Hier is een compleet en correct antwoord voor `double_char`: + +```python +def double_char(str): + result = '' + for c in str: + result += c*2 + return result +``` + +Je mag dit overtypen, of plakken, en controleren dat de tests werken. + +Zorg dat je de ***strategie*** voor deze oplossing voor `double_char` begrijpt: + +* We beginnen met een verzamelvariabele met een geschikte startwaarde + * Hier is dat de variabele `result`, die begint op de waarde `''` (de lege string) +* We schrijven een lus die het gewenste resultaat verzamelt +* Daarna geven we het resultaat terug + + +Maak alle 12 oefeningen. De gemaakte code kan je kopieren naar een python bestand om het te bewaren. + diff --git a/problems/context/4_functies.md b/problems/context/4_functies.md index efa1becf..a6b7ade8 100644 --- a/problems/context/4_functies.md +++ b/problems/context/4_functies.md @@ -21,6 +21,6 @@ c. Schrijf een functie `add_up(l)` die een lijst `l` met getallen accepteert, al a. Schrijf een functie `present(l, c)` die een lijst `l` en variabele `c` accepteert en `True` teruggeeft of `c` in `l` zit en anders `False` teruggeeft. -c. Schrijf een functie `count(l, c)` die een lijst `l` en variabele `c` accepteert en teruggeeft hoe vaak `c` in `l` zit. +b. Schrijf een functie `count(l, c)` die een lijst `l` en variabele `c` accepteert en teruggeeft hoe vaak `c` in `l` zit. -b. Schrijf een functie `find(l, c)` die een lijst `l` en variabele `c` accepteert en de *eerste* index teruggeeft waar `c` staat. Zit `c` niet in `l` dan geeft de functie -1 terug. \ No newline at end of file +c. Schrijf een functie `find(l, c)` die een lijst `l` en variabele `c` accepteert en de *eerste* index teruggeeft waar `c` staat. Zit `c` niet in `l` dan geeft de functie -1 terug. \ No newline at end of file diff --git a/problems/context/8_list_comprhensions.md b/problems/context/8_list_comprhensions.md new file mode 100644 index 00000000..2784a5be --- /dev/null +++ b/problems/context/8_list_comprhensions.md @@ -0,0 +1,425 @@ +# Numerieke integratie + +In deze opgave schrijf je een Python programma dat de oppervlakten onder willekeurige wiskundige functies kan berekenen (wiskundigen noemen dit *integralen*). + +Door dit te doen oefen je: + +* het schrijven van Python-functies +* het gebruik van *list comprehensions* +* het schrijven van hulpfuncties en opbouwen van grotere functies uit kleinere functies + +Bovendien bereken je antwoorden waar wiskundigen geen formule voor hebben... en daar heb je niets meer dan vermenigvuldigen voor nodig! + +## Een begin + +Begin met een nieuw bestand `wk3ex3.py` en plak daar de onderstaande code in: + +```python + +# hiermee krijgen we functies als sin en cos... +from math import * + +def main(): + """Main functie""" + +def testing(): + """test functie""" + assert lc_mult(4) == [0, 2, 4, 6] + assert lc_idiv(4) == [0, 0, 1, 1] + assert lc_fdiv(4) == [0.0, 0.5, 1.0, 1.5] + + +# twee extra functies (die niet in de module math hierboven zitten) + + +def dbl(x): + """Doubler! argument: x, a number""" + return 2 * x + + +def sq(x): + """Squarer! argument: x, a number""" + return x ** 2 + + +# voorbeelden om aan list comprehensions te wennen... + + +def lc_mult(n): + """This example accepts an integer n + and returns a list of integers + from 0 to n-1, **each multiplied by 2** + """ + return [2 * x for x in range(n)] + + +def lc_idiv(n): + """This example accepts an integer n + and returns a list of integers + from 0 to n-1, **each divided by 2** + WARNING: this is INTEGER division...! + """ + return [x // 2 for x in range(n)] + + +def lc_fdiv(n): + """This example accepts an integer n + and returns a list of integers + from 0 to n-1, **each divided by 2** + NOTE: this is floating-point division...! + """ + return [x / 2 for x in range(n)] + + +# Hier begin je met de functies voor deze opgave: + + +# Stap 1, deel 1 +def unitfracs(n): + """Vergeet niet deze docstring te verbeteren! + """ + pass # vervang deze regel (pass is een Python-statement dat niets doet) +``` + +### List comprehensions + +:::{admonition} Integer- en floating-pointdeling +:class: notice + +Merk op dat de aanroepen van `lc_idiv(10)` en `lc_fdiv(10)` *verschillende* lijsten teruggeven. De eerste gebruikt *integer*deling (de resultaten worden omlaag afgerond) en de tweede *floating-point*deling (dus de resultaten blijven een kommagetal). +::: + +### Aanpassing + +Pas de functie `lc_idiv` aan zodat de body gelijk is aan: + +```python +return [float(x // 2) for x in range(n)] +``` + +Bedenk voordat je de functie uitvoert of `lc_idiv(10)` nog hetzelfde of anders dan daarvoor. Zal het `assert` statement hierboven nog slagen, of zal het een fout geven? + +Typ nu `run wk3ex3.py` in IPython en probeer `lc_idiv(10)`. Komt dit overeen met wat je verwachtte? Je hoeft hier niets over op te schrijven, maar wees er zeker van dat duidelijk is waarom deze nieuwe uitvoer is zoals het is! + +Zie verder voorbeelden van [list comprehensions](/practice/2_list_comprehension) voor meer oefeningen! + +## Integreren + +Integreren wordt soms beschreven als het vinden van het gebied tussen een wiskundige functie en de horizontale (x-)as. + +Meer in het algemeen is een integraal eenvoudigweg de *som* van de resultaten van een numerieke functie, dat wil zeggen de y-waarden over een gekozen bereik. + +Dit is belangrijk omdat het aan de hand van één getal een soort van samenvatting geeft van wat de functie "doet" over een interval (een bepaald bereik). Het wordt ook gebruikt om te bepalen hoe verschillende krachten (zwaartekracht, trekkracht, ...) zich gedragen op een bepaald punt, oppervlak of object over een periode van tijd. Het is ook essentieel bij het definiëren van de "gemiddelde waarde" van een functie over een interval of een gebied. Integralen kunnen worden gebruikt om een vliegtuigvleugel te ontwerpen, het weer te voorspellen of te berekenen hoeveel een bankrekening over tien jaar waard zal zijn, zelfs als de rentetarieven veranderen. + +Bijvoorbeeld, denk aan de functie `dbl` hierboven (maar nu met een betere docstring): + +```python +def dbl(x): + """Argument: a number x (int or float) + Return value: twice the argument + """ + return 2 * x +``` + +Stel je voor dat je een schatting wilt maken van de integraal van `dbl` op het interval tussen `0.0` en `10.0`. Je zou de volgende (ruwe) benadering kunnen maken met rechthoeken: + +![Integraal](images/listcomprehensions/y2x.png) + +1. Hier wordt het interval verdeeld over 4 delen. De lijst `[0, 2.5, 5, 7.5]` bevat de x-waarde van de *linkergrenzen* van elk van de vier subintervallen. + +2. We bepalen hierna het resultaat van `dbl(x)` voor elke mogelijke `x` uit de lijst hierboven. We noemen deze waarden `y`: `y = [0, 5, 10, 15]`. + +3. We tellen nu de oppervlaktes van de rechthoeken op waarvan de linkerbovenhoek (dus de hoogte) op deze `y`-waarden ligt. Elke rechthoek loopt door tot de x-as. De breedte van elke rechthoek is `2.5`, omdat de vier rechthoeken met gelijke breedte in totaal een breedte van `10` moeten hebben. + +4. We bepalen de oppervlaktes van de rechthoeken op de gebruikelijke manier. Hun hoogtes staan in de lijst `y` en hun breedtes zijn `2.5`, dus de totale oppervlakte is + + ```python + 0 * 2.5 + 5 * 2.5 + 10 * 2.5 + 15 * 2.5 + ``` + + of + + ```python + (0 + 5 + 10 + 15) * 2.5 + ``` + + wat gelijk is aan `(30) * 2.5`, of `75`. + +Bedenk, dit is een *hele* ruwe benadering van het "oppervlakte onder de lijn", oftewel de integraal. Maar als we de breedte van de rechthoeken kleiner maken, dan zal hun som dat gebied zo goed kunnen benaderen als we zouden willen! + +Belangrijker nog is dat aan de hand van dit voorbeeld we een algemene aanpak kunnen uitwerken om de integraal van een functie over een gegeven interval te benaderen: + +1. Deel het interval op in `n` gelijke delen en maak een lijst met de corresponderende `x`-waarden (deze worden het argument van de functie) + +2. Bereken de `y`-waarden (resultaten) van de functie voor elke waarde van `x` uit stap 1. + +3. Bereken de oppervlakte van de rechthoeken onder de kromme. Hun hoogtes zijn de `y`-waarden, hun breedtes de afstand tussen de `x`-waarden. + +4. Tel de oppervlaktes bij elkaar op en geef het resultaat terug. Dit is de integraal, of een schatting daarvan die verder onbeperkt nauwkeurig gemaakt kan worden. + +In de rest van deze opgave ga je de functies schrijven voor elk van deze vier stappen, en je gebruikt vervolgens deze functies om vragen over de resultaten te beantwoorden. + +## Stap 1: De `x`-waarden berekenen + +De eerste stap betreft het berekenen van de `x`-waarden die we gaan gebruiken. Allereerst ga je de functie `unitfracs(n)` schrijven en vervolgens de functie `scaledfracs(low, hi, n)`. + +### De functie `unitfracs` + +Bekijk hoe `unitfracs(n)` zal moeten gaan werken: + +```ipython +In: unitfracs(2) +Out: [0.0, 0.5] + +In: unitfracs(4) +Out: [0.0, 0.25, 0.5, 0.75] + +In: unitfracs(5) +Out: [0.0, 0.2, 0.4, 0.6, 0.8] + +In: unitfracs(3) +Out: [0.0, 0.3333333333333333, 0.6666666666666666] + +In: unitfracs(10) +Out: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] +``` + +Zoals de naam al aangeeft zal de functie een lijst van linkergrenzen (en dit zijn breuken, of *fracties*) op gelijke afstand van elkaar en met elk een waarde tussen `0` en `1` teruggeven. Dat wil zeggen, als `n` 5 is dan zal de lijst 5 waarden moet bevatten, namelijk `0/5`, `1/5`, `2/5`, `3/5` en `4/5`. + +:::{admonition} Gebruik `lc_fdiv` als begin +:class: tip + +Kopieer en plak de voorbeeldfunctie `lc_fdiv` en pas deze aan om `unitfracs` te schrijven. Je hoeft maar een *enkel* karakter aan te passen in deze code! Vergeet niet de docstring van `unitfracs` aan te passen om aan te geven wat de functie doet. +::: + +Schrijf ten slotte ten minste drie `assert` statements om tests te implementeren, je mag een aantal van de voorbeelden hierboven gebruiken. + +### De functie `scaledfracs` + +Bekijk nu hoe de functie `scaledfracs(low, hi, n)` zal moeten gaan werken: + +```ipython +In : scaledfracs(10, 30, 5) +Out: [10.0, 14.0, 18.0, 22.0, 26.0] + +In : scaledfracs(41, 43, 8) +Out: [41.0, 41.25, 41.5, 41.75, 42.0, 42.25, 42.5, 42.75] + +In : scaledfracs(0, 10, 4) +Out: [0.0, 2.5, 5.0, 7.5] +``` + +Schrijf `scaledfracs(low, hi, n)`. De functie geeft een lijst met `n` linkergrenzen terug die evenredig verdeeld zijn over een interval dat van `low` tot `hi` loopt. + +Schrijf ook `assert` statements om de voorbeelden hierboven te testen. + +:::{admonition} Hoe `scaledfracs` uit te werken +:class: tip + +De functie `scaledfracs` is lastig om uit te werken, we geven je extra uitleg en tips. Om te beginnen, *gebruik* `unitfracs`, de functie die al hebt geschreven. De volgende regel geeft een idee hoe je dit zou kunnen doen: + +```python +return [... for x in unitfracs(n)] +``` + +Op deze manier hoef je het werk van `unitfracs` niet opnieuw te doen! Je zal ook misschien hebben zien dat `scaledfracs` erg lijkt op de functie `interp` die je eerder hebt geschreven, en dat klopt! + +De functie `interp` hoef je niet te gebruiken, maar je kan er wel ideeën uit opdoen! Hier is de functie, ter herinnering: + +```python +def interp(low, hi, fraction): + """Returns a value frac of the way from low to hi""" + return low + (hi - low) * fraction +``` + +Merk op dat de rol van `fraction` hierboven is om aan te geven hoe ver je op de weg bent van `low` naar `hi` en dat is precies wat `x` doet in de list comprehension! + +Vergeet niet een docstring toe te voegen die beschrijft wat `scaledfracs` doet. +::: + +## Stap 2: De `y`-waarden berekenen + +De functie `scaledfracs` kan allerlei lijsten met `x`-waarden op gelijke afstand maken. + +Je moet nu de `y`-waarden (de resultaten) van een functie op elk van deze `x`-posities berekenen. Ook hier gebruik je list comprehensions om dit proces simpel en snel te maken! + +Het doel is om elke functie te kunnen verwerken, maar we beginnen met een concreet voorbeeld dat later verder uitgebreid gaat worden. + +### De functie `sqfracs` + +Schrijf een functie `sqfracs(low, hi, n)` die als volgt werkt: + +```ipython +In [1]: sqfracs(4, 10, 6) +Out[1]: [16.0, 25.0, 36.0, 49.0, 64.0, 81.0] + +In [2]: sqfracs(0, 10, 5) +Out[2]: [0.0, 4.0, 16.0, 36.0, 64.0] +``` + +Deze functie `sqfracs` lijkt heel erg op `scaledfracs`, alleen is elke waarde *gekwadrateerd*. + +:::{admonition} Gebruik `scaledfracs` +:class: tip + +Gebruik hier `scaledfracs` hier. `sqfracs` kan `scaledfracs` op dezelfde manier gebruiken als de manier waarop `scaledfracs` gebruikmaakt van `unitfracs`! Bekijk dit stukje code: + +```python +for x in scaledfracs(low, hi, n) +``` +::: + +Zoals altijd, schrijf aantal `assert` statements om `sqfracs` te testen. Bedenk ook zelf een aantal voorbeelden om te testen! + +### De functie `f_of_fracs` + +Schrijf een functie `f_of_fracs(f, low, hi, n)` die als volgt werkt: + +```ipython +In: f_of_fracs(dbl, 10, 20, 5) +Out: [20.0, 24.0, 28.0, 32.0, 36.0] + +In: f_of_fracs(sq, 4, 10, 6) +Out: [16.0, 25.0, 36.0, 49.0, 64.0, 81.0] + +In: f_of_fracs(sin, 0, pi, 2) # de sinusfunctie +Out: [0.0, 1.0] +# de waardes hierboven zijn afgeronde versies van +# wat je misschien zelf ziet... +``` + +Merk op dat `f_of_fracs` als eerste argument een *functie* accepteert, dat kan in Python (zie ook [list comprehensions](/practice/2_list_comprehension) voor voorbeelden hoe je functies als argument kan doorgeven). + +Je kan `sqfracs` kopiëren en plakken als *basis* voor deze functie, je hoeft maar een paar tekens aan te passen! Gebruik net als in de eerdere delen van deze opgave de voorbeelden hierboven om ten minste drie `assert` statements voor `f_of_fracs` te schrijven. + +## Stap 3: berekenen en combineren + +Je hebt nu functies die zowel de `x`-, en belangrijker nog, de `y`-waarden voor een functie berekenen op gelijke afstanden. + +Je gaat nu een functie `integrate(f, low, hi, n)` schrijven die de uiteindelijke gewenste waarde teruggeeft, de integraal over `f` van `low` naar `hi`, benaderd met `n` stappen. + +Bekijk de voorbeelden en hints hieronder. Zoals je kan zien aan de voorbeelden geeft `integrate` geen lijst terug, maar geeft het een enkel floating-point getal terug: + +```ipython + +In: integrate(dbl, 0, 10, 4) +Out: 75.0 # het voorbeeld van het begin van deze opgave + +In: integrate(dbl, 0, 10, 1000) +Out: 99.9 # afgerond van 99.8999... (precieze waarde: 100) + +In: integrate(sq, 0, 3, 1000000) # Python heeft wel even nodig voor een miljoen stappen +Out: 8.9999865000044963 # bijna! (precieze waarde: 9.0) + +In: integrate(math.sin, 0, math.pi, 1000) +Out: 1.9999983550656628 # best goed! (precieze waarde: 2.0) +``` + +Maak je geen zorgen over kleine fouten in de meest rechtse cijfers achter de komma. Deze komen door afrondfouten, dit kan je niet voorkomen omdat de nauwkeurigheid van floating-point getallen beperkt is. + +Om te beginnen is hier de *signature* (de regel met `def`) en de docstring voor `integrate`. Voel je vrij dit te gebruiken: + +```python +def integrate(f, low, hi, n): + """Integrate returns an estimate of the definite integral + of the function f (the first argument) + with lower limit low (the second argument) + and upper limit hi (the third argument) + where n steps are taken (the fourth argument) + + integrate simply returns the sum of the areas of rectangles + under f, drawn at the left endpoints of n uniform steps + from low to hi + """ + + +assert integrate(dbl, 0, 10, 4) == 75 +assert integrate(sq, 0, 10, 4) == 2.5 * sum([0, 2.5*2.5, 5*5, 7.5*7.5]) +``` + +Deze keer gaan we "test-driven development" gebruiken, waarin je *eerst* de tests schrijft en daarna zorgt dat je functie aan de tests voldoet. Daarom hebben we de `assert` statements hierboven toegevoegd! Merk op dat de tweede `assert` een voorbeeld is van hoe je kan testen ook als je niet weet wat het numerieke antwoord is! + +:::{admonition} Hoe `integrate` uit te werken +:class: tip + +Om te beginnen, gebruik GEEN list comprehensions, er wordt hier geen lijst gemaakt. Gebruik in plaats daarvan `f_of_fracs` om een lijst van hoogtes te maken. + +Onthoud dat het resultaat van `f_of_fracs` een grote lijst `y`-waarden is! Je moet deze `y`-waarden (hoogtes) vermenigvuldigen met de breedtes van de rechthoeken. Maar alle breedtes zijn hetzelfde! Je kan dus eerst de hoogtes optellen en daarna vermenigvuldigen met de breedte! + +Gebruik de ingebouwde Python functie `sum`. `sum(L)` geeft de som van de elementen in `L` terug. Een paar voorbeelden van hoe `sum` werkt: + +```ipython +In : sum([10, 4, 1]) +Out: 15 + +In : sum(range(0, 101)) +Out: 5050 +``` +::: + +Als jouw functie `integrate`werkt, gefeliciteerd! Je hebt een algemeen bruikbare routine geschreven om integralen te berekenen voor elke functie die je kan berekenen (ook waarvoor geen formule van de integraal bestaat, dat wil zeggen, waarvoor zelfs de beste wiskundigen ter wereld niet weten hoe je een precies antwoord moet berekenen). + +Je gaat nu `integrate` toepassen... + +## Vragen + +Neem jouw antwoorden op in het bestand `wk3ex3.py` als commentaar (met het `#` teken) of *eenvoudiger* door ze in een triple-quoted string op te nemen die je kent van docstrings (omdat daar regelafbrekingen in mogen voorkomen) + +Je hoeft geen antwoord te geven op de "nulde" vraag, we hebben het antwoord hier neergezet als voorbeeld van hoe je de andere twee kan beantwoorden: + +### Vraag 0 (voorbeeld) + +Leg uit waarom `integrate(dbl, 0, 10, n)` steeds dichter naar 100 nadert als `n` groter wordt. + +Het antwoord zou er als volgt uit kunnen zien (merk op dat dit een matig antwoord is, omdat het niet echt de vraag beantwoordt): + +```python +"""Vraag 0. + +De waarde van integrate(dbl, 0, 10, n) waarbij n steeds groter wordt is +gelijk aan de oppervlakte onder de functie dbl, de lijn y = 2*x tussen +x = 0.0 en x = 10.0 + +Deze waarde is de oppervlakte van de driehoek in de afbeelding bovenaan +de pagina met de opgave. + +Die oppervlakte is 100, omdat de hoogte van de driehoek 20 is en de +breedte 10 is. + +Voor een driehoek geldt dat A = 0.5*h*w. +""" +``` + +### Vraag 1 + +Zoals opgemerkt is de exacte waarde van de integraal van de functie `dbl`, `y = 2x`, van `x = 0,0` tot `x = 10,0` gelijk aan 100, wat de oppervlakte is van de driehoek in de afbeelding bovenaan deze pagina. Die oppervlakte is 100, want de hoogte van de driehoek is 20 en de breedte is 10. + +De oproepen `integrate(dbl, 0, 10, 4)` en `integrate(dbl, 0, 10, 1000)`, zoals hierboven weergegeven, geven als resultaat waarden die *kleiner* dan 100 zijn. + +Leg in één zin uit waarom `integrate` altijd een *onderschatting* maakt van de exacte waarde van deze specifieke integraal. + +Als een vervolgvraag, kan je een functie bedenkten waarvan de integraal altijd *overschat* wordt op hetzelfde bereik, van `0` tot `10`? (Als je hier niet uitkomt kan je eerst naar de volgende vraag kijken.) + +### Vraag 2 + +De volgende functie `c` volgt de bovenste helft van een cirkel met straal 2. + +```python +def c(x): + """c is a semicircular function of radius two""" + return (4 - x ** 2) ** 0.5 +``` + +Voeg deze functie toe aan jouw bestand `wk3ex3.py` en controleer onderstaande waardes. + +```ipython +In [1]: integrate(c, 0, 2, 2) +Out[1]: 3.732 # afgerond... + +In [2]: integrate(c, 0, 2, 20) +Out[2]: 3.228 # afgerond... +``` + +Bepaal de waardes van `integrate(c, 0, 2, 200)` en `integrate(c, 0, 2, 2000)` en noteer ze in jouw antwoord. + +Als `n` naar oneindig gaat (dat wil zeggen, steeds groter wordt), wat gebeurt er dan met de waarde van deze integraal? Waarom? diff --git a/problems/context/9_pi_pijlen.md b/problems/context/9_pi_pijlen.md new file mode 100644 index 00000000..0d8f5fa9 --- /dev/null +++ b/problems/context/9_pi_pijlen.md @@ -0,0 +1,149 @@ +# Pi met pijltjes + +![Dartboard](images/pi_pijlen/500px-Dartboard.png) + +Het is misschien verbazingwekkend dat het mogelijk is om de wiskundige constante π te berekenen zonder dat je andere operaties nodig hebt dan tellen, optellen en vermenigvuldigen. In deze opgave ga je twee functies schrijven om pi (`3.14159...`) te benaderen door *het gooien van pijltjes*. + +Zie ook [Calculating Pi with Darts](https://www.youtube.com/watch?v=M34TO71SKGk) hoe je dit zou kunnen simuleren met échte pijltjes! + +## Pi met pijltjes berekenen: achtergrond + +Stel je een cirkel voor die ingeschreven is in een vierkant die het gebied met `-1 ≤ x ≤ 1` en `-1 ≤ y ≤ -1` beslaat. De oppervlakte van de ingeschreven cirkel, waarvan de straal `1.0` is, moet dan π zijn: de oppervlakte van een cirkel is immers gelijk aan π*r*2, en de straal *r* is hier `1.0`. + +:::{admonition} Ingeschreven cirkel +:class: notice + +Een *ingeschreven* cirkel raakt de randen van het omliggende vierkant precies; als het vierkant dus zijden van lengte 2 heeft, is de diameter van de cirkel ook 2. +::: + +Als je pijltjes gooit op willekeurige punten in het vierkant zullen maar een deel daarvan de ingeschreven cirkel raken. De verhouding + +``` +oppervlakte van de cirkel / oppervlakte van het vierkant +``` + +kan benaderd worden door de verhouding + +``` +aantal pijltjes dat de cirkel raakt / totaal aantal geworpen pijltjes +``` + +Als je meer en meer pijltjes gooit benadert de tweede verhouding hierboven steeds dichter de eerste verhouding. Aangezien drie van de vier waardes hierboven bekend zijn kunnen we ze gebruiken om de oppervlakte van de cirkel te benaderen. Dit kan weer gebruikt worden om π te benaderen. + +### Pijltjes gooien ontwerpen... + +Om een pijltje te gooien, moet je willekeurige x- en y-coördinaten genereren tussen `-1.0` en `1.0`. Zorg dat je de regel + +```python +import random +``` + +bovenaan je bestand toevoegt. Als je dit doet, heb je nu de beschikking tot de functie + +```python +random.uniform(-1.0, 1.0) +``` + +Deze regel geeft een floating-pointwaarde in het bereik van `-1.0` tot en met `1.0`. Je kan bijvoorbeeld schrijven: + +```python +x = random.uniform(-1.0, 1.0) +``` + +## De functie `throw_dart` + +Met dit in het achterhoofd is het handig om een hulpfunctie `throw_dart()` te schrijven die + +* Eén "pijlte" gooit in het vierkant door een willekeurige `x`- en een willekeurige `y`-coördinaat te genereren tussen -1 en 1 +* Bepaalt of dat pijltje in de cirkel met straal 1 rond de oorsprong valt; je kan hier de functie `math.sqrt` gebruiken om dit te controleren, maar dat is niet strikt noodzakelijk! +* `True` teruggeeft als het pijltje de cirkel raakt en `False` teruggeeft als het pijlte de cirkel mist +* Onthoud dat het pijltje altijd het vierkant raakt, door de manier waarop de worp ontworpen is... + +:::{admonition} Valt het pijltje binnen de cirkel?" +:class: tip + +Het pijltje valt binnen de cirkel als de afstand tot het middenpunt, in dit geval (0, 0), kleiner is dan of gelijk is aan de straal. Je kan de afstand berekenen met de stelling van Pythagoras: de afstand van een punt (*x*, *y*) tot het punt (0, 0) is gelijk aan de wortel van *x*2 + *y*2 +::: + +Deze hulpfunctie kan je gebruiken voor de beide hoofdfuncties van deze opgave: `for_pi` en `while_pi`. + +Hoe je je Monte-Carlosimulatie ook ontwerpt moet je onthouden dat je als altijd een docstring opneemt in beide functies die uitlegt hoe de functie werkt! + +## De functie `for_pi` + +De functie `for_pi(n)` moet een positieve integer `n` als parameter meekrijgen. + +Ze moet `n` pijltjes in het vierkant "gooien". + +Na elk pijltje dat gegooid is moet de functie het volgende afdrukken: + +* Het aantal pijltjes dat tot nu toe gegooid is. +* Het aantal pijtljes dat tot nu toe de cirkel ***geraakt*** heeft. +* De hieruit volgende schatting voor π. + +**Returnwaarde**; *vergeet deze niet!* + +De functie `for_pi` moet de *uiteindelijke schatting voor π* na `n` worpen teruggeven. + +Hier is een voorbeelduitvoer om te laten zien hoe `for_pi` moet werken: + +* Je eigen uitvoer zal variëren vanwege de willekeurigheid... +* Het moet echter de echte waarde van π benaderen als het aantal gegooide pijltjes `n` groter wordt + +```ipython +In [1]: for_pi(10) +1 raak van 1 worpen dus pi is 4.0 +2 raak van 2 worpen dus pi is 4.0 +3 raak van 3 worpen dus pi is 4.0 +4 raak van 4 worpen dus pi is 4.0 +4 raak van 5 worpen dus pi is 3.2 +5 raak van 6 worpen dus pi is 3.33333333333 +6 raak van 7 worpen dus pi is 3.42857142857 +6 raak van 8 worpen dus pi is 3.0 +7 raak van 9 worpen dus pi is 3.11111111111 +8 raak van 10 worpen dus pi is 3.2 + +Out[1]: 3.2 +``` + +## De functie `while_pi(error)` + +De functie `while_pi(error)` moet een positieve floating-pointwaarde `error` als parameter meekrijgen. + +Ze moet dan pijltjes naar het vierkant gooien totdat het *absolute verschil* tussen de schatting die de functie voor π maakt en de echte waarde van π minder is dan `error`. + +Je functie `while_pi` heeft de echte, bekende waarde van π nodig om te bepalen of de schatting binnen de foutmarge ligt! Dit zou niet kunnen als de constante die we benaderen echt onbekend is, maar in dit geval kan je de regel + +```python +import math +``` + +toevoegen aan je code en de waarde `math.pi` gebruiken als de echte waarde voor π. + +Net als de functie `for_pi` moet je functie `while_pi` na elke worp het volgende afdrukken: + +* Het aantal pijltjes dat tot nu toe gegooid is. +* Het aantal pijtljes dat tot nu toe de cirkel ***geraakt*** heeft. +* De hieruit volgende schatting voor π. + +**Returnwaarde**; *vergeet deze niet!* + +De functie `while_pi` moet *het aantal gegooide pijltjes* teruggeven dat nodig was om de gevraagde +nauwkeurigheid te bereiken. + +Hier is een voorbeelduitvoer om te laten zien hoe `while_pi` werkt: + +```ipython +In [7]: while_pi(0.1) +1 raak van 1 worpen dus pi is 4.0 +2 raak van 2 worpen dus pi is 4.0 +3 raak van 3 worpen dus pi is 4.0 +4 raak van 4 worpen dus pi is 4.0 +5 raak van 5 worpen dus pi is 4.0 +5 raak van 6 worpen dus pi is 3.33333333333 +6 raak van 7 worpen dus pi is 3.42857142857 +7 raak van 8 worpen dus pi is 3.5 +7 raak van 9 worpen dus pi is 3.11111111111 + +Out[7]: 9 +``` diff --git a/problems/context/images/listcomprehensions/y2x.png b/problems/context/images/listcomprehensions/y2x.png new file mode 100644 index 00000000..8970cdc7 Binary files /dev/null and b/problems/context/images/listcomprehensions/y2x.png differ diff --git a/problems/context/images/pi_pijlen/500px-Dartboard.png b/problems/context/images/pi_pijlen/500px-Dartboard.png new file mode 100644 index 00000000..4e4d3010 Binary files /dev/null and b/problems/context/images/pi_pijlen/500px-Dartboard.png differ diff --git a/problems/instap/8_list_comprehensions.md b/problems/instap/8_list_comprehensions.md new file mode 100644 index 00000000..c250a2fc --- /dev/null +++ b/problems/instap/8_list_comprehensions.md @@ -0,0 +1,58 @@ +# List Comprehensions + +Wat voor lijst `L` krijg je met onderstaande listcomprehensions + +## opgave 1 +```python +L = [x for x in range(10)] +``` + +## opgave 2 +```python +L = [x for x in range(5,10)] +``` + +## opgave 3 +```python +L = [x for x in range(10) if x != 5] +``` + +## opgave 4 +```python +L = [x for x in range(10) if x % 3 == 0] +``` + +## opgave 5 +```python +L = [x for x in range(10) if x % 3 == 0] +``` + +## opgave 6 +```python +fruits = ["appel", "banaan", "kiwi", "citroen", "mango" ] +L = [x for x in fruits if "a" in x] +``` + +## opgave 7 +```python +fruits = ["appel", "banaan", "kiwi", "citroen", "mango" ] +L = [fruits[x] for x in range(len(fruits)) if fruits[x] <= "c"] +``` + +## opgave 8 +```python +fruits = ["appel", "banaan", "kiwi", "citroen", "mango" ] +L = [x for x in range(len(fruits)) if fruits[x] <= "c" and x % 2 == 0] +``` + +## opgave 9 +```python +fruits = ["appel", "banaan", "kiwi", "citroen", "mango" ] +L = [x for x in fruits if len(x) > 5] +``` + +## opgave 10 +```python +fruits = ["appel", "banaan", "kiwi", "citroen", "mango" ] +L = [len(x) for x in fruits] +``` \ No newline at end of file