04-09-2017 door: Daan Veraart
De wet van Hofstadter luidt: alles duurt langer dan je denkt, zelfs als je rekening houdt met de Wet van Hofstadter. Niet alleen is dat een leuk voorbeeld van recursie, iets wat zeker nog ter sprake zal komen in dit blog. Het is ook zeker van toepassing op leren (duel)leren deel 4. Om jou, de trouwe lezer van de blogs, niet in een zwart gat te laten vallen, deze intermissie.
Rode draad technisch gezien ben ik nog steeds bezig met uitleggen wat programmeren nou precies is. Waar veel mensen niet bij stil staan, is dat een onderdeel van programmeren is naar al bestaande code kijken en zien wat het doet, of zou moeten doen. Nu denk je misschien, maar als je kan programmeren dan is het toch makkelijk te zien om wat er gebeurt? Maar programmeren is niet zo binair als je zou denken, er zijn vaak een hele array aan wegen die naar Rome leiden, en iemand anders kan zomaar een route hebben genomen die je nooit zelf zou hebben bedacht. Voorbeeldje:
Stel er moet een functie komen die een positief heel getal krijgt en dan de faculteit berekent, maar in plaats van vermenigvuldigen telt hij op, dus als hij 4 krijgt moet hij geven: 4+3+2+1 = 10.
Dat zou je op de volgende manier kunnen doen:
var x = function(getal) { var a = 0 for (var i = 1; i <= getal; i++) { a += i; } return a; }
Maar ook:
var y = function(getal) { if (getal == 1) return 1 else return getal + y(getal - 1) }
Dat is overigens recursie, eigenlijk heel simpel, recursie is als een functie zichzelf aanroept.
Dit waren al 2 heel verschillende manieren, de eerste is een for-loop, de tweede is een recursieve oplossing. Maar het kan ook zo:
var z = function(getal) { if (getal % 2 == 0) return (getal + 1) * (getal / 2) else return getal * ((getal + 1) / 2) }
Of zelfs:
var z2 = function(getal) { return (getal * (getal + 1)) / 2 }
O! Dat heb ik nog helemaal niet uitgelegd, in leren proberen heb ik wel functies en variabelen laten zien, maar geen functievariabelen. Dit zijn functie-variabelen, in javascript mag je in een variabele niet alleen een getal of een stuk tekst opslaan, mag je er ook hele functies in opslaan. Groot voordeel daarvan is dat je het makkelijk kan testen. Je kunt de stukjes code hierboven makkelijk zelf testen. Er van uitgaande dat je dit in een moderne browser, zoals Chrome, Firefox, Safari of Edge op je PC of laptop leest, kan je op F12 drukken, naar je console gaan dit er in knippen en plakken en testen kijk maar:
Dat is vooral handig omdat we weer een stukje lezersparticipatie gaan doen. Zoals ik eerder al heb gezegd, de enige manier om te leren programmeren is door te doen. Dit keer hoef je niet zelf hele stukken code te bedenken, maar ik geef je een stukje code. Ik zeg wat het moet doen en dan aan jou de taak om te kijken wat er mis is met de code. Het doel is de code begrijpen door het te lezen en aangezien ook niet alle trouwe lezers professionele programmeurs zijn, zijn het allemaal logicafouten en geen missende haakjes ofzo. Voorbeeldje? Voorbeeldje!
De wet van Hofstadter luidt…. Of wacht dat had ik al verteld. Ondanks dat het geen zin heeft om rekening te houden met de wet van Hofstadter, gaan we het toch proberen. Ik heb een array (dat is een lijst, deze lijst staat in javascript tussen blokhaken en de elementen zijn gescheiden met een komma. Dat is wel heel kort door de bocht, wacht kleine intermissie.)
Intermissie:
Een array is een veel gebruikt object in programmeertalen. Een array is zoals gezegd een lijst van dingen, die je makkelijk op kunt slaan en waar je makkelijk door heen kunt ‘bladeren’.
Voorbeeld: Stel je bent leraar wiskunde, of als wiskunde je niet ligt een willekeurig ander vak, behalve gym, gym heeft geen proefwerken, en ik ga het over een proefwerk hebben. Je hebt net een proefwerk gemaakt en je hebt van je hele klas de cijfers van het proefwerk. Dan zou je voor elke leerling apart op kunnen slaan welk cijfer ze hebben gehaald in elk een aparte variabele:
var cijferDaan = 9 var cijferRoelof = 9.5 var cijferMarlies = 10
Enzovoort.
Maar dat is niet erg handig, omdat als nu iets met alle cijfers wilt doen je steeds elke afzonderlijke variabele moet aanroepen. Wat je ook kan doen is er één lijst van maken:
var cijfers = [9,9.5,10];
Nu zitten alle cijfers in één object; cijfers. Ik weet nu niet meer van wie welk cijfer is, dat is wel op te lossen, maar dit is een simpel voorbeeld dus alleen de cijfers zijn genoeg. Laten we er voor het voorbeeld nog een paar cijfers aan toevoegen:
var cijfers = [9,9.5,10,6,7.7,4.5,3.5,3,4,5,3,7,6.5,5.5,8,2,1,1.5,8.5];
Zoals je kunt zien heeft niet iedereen het zo goed gemaakt. Nu we een array hebben kunnen we er makkelijk een aantal dingen mee doen. We kunnen bijvoorbeeld makkelijk kijken hoeveel cijfers het zijn:
Een array begint te tellen met 0, als we willen weten wat er op plaats 0 staat, kan dat op de volgende manier:
Je kunt ook makkelijk door een array heen bladeren een voorbeeldje om het totaal van alle cijfers te berekenen:
(Let op ik begin met 0 en tel daar 1 bij op zolang het kleiner is dan de lengte van de lijst, omdat de lijst met 0 begint is het 19de element cijfers[18])
Die 105.2 kun je dan weer door 19 delen en dan zien we dat het gemiddelde cijfer een 5.5 is. Waar dan ook vaak naar gekeken wordt is het middelste cijfer, dat is het cijfer dat in het midden staat als de lijst geordend, gelukkig kan dat ook makkelijk:
Dat komt wel heel mooi uit, ik ben duidelijk niet goed in willekeurige data bedenken. Maar het proefwerk is niet heel goed gemaakt, dus we passen de sleutel met een half punt naar boven aan:
Als je goed op hebt gelet zie je iets raars, ik heb net de getallen gesorteerd, en de lijst is ook wel gesorteerd maar de 10.5 staat niet op de plaats waar je hem zou verwachten. Typisch iets wat we eerst hadden moeten controleren, omdat we net overal een half bij opgeteld hebben is het extra verwarrend dus we doen het even opnieuw:
Zie je al wat er gebeurt? De lijst is wel gesorteerd, maar niet op grote van getallen, maar op alfabetische volgorde, en dan komt de 1 in 10 voor de 2, iets wat de standaard instelling van de sort() functie is, kijk maar:
Dat zijn van die kleine programmeerfoutjes die er in kunnen sluipen, maar dat zijn dus arrays. Einde van de intermissie.
Ik heb dus een array:
var uren = [1,2,6,0,1.5,2.5,6,3.75]
(Die kun je al meteen zo in je console plakken.) In deze array staan van alle taken die ik nog moet doen hoeveel uur het gaat kosten. Ik ben benieuwd hoeveel tijd het me in totaal gaat kosten, maar omdat ik weet van de wet van Hofstadter hou ik er rekening mee dat alles wat ik nog moet gaan doen 3 kwartier langer duurt dan dat ik tot nu toe heb begroot. De functie die dat berekent is als volgt:
var a = function(lijst) { var totaal = 0; //ga elke waarde in lijst af en tel er 0.75 bij op for (var i = 0; i < lijst.length; i++) { totaal += lijst[i] + 0.75 } return totaal; }
Nu is aan jou de vraag, waarom is dat fout? Omdat dit een voorbeeld is, ga ik het meteen voorzeggen: Als je kritisch naar de lijst met uren kijkt zie je dat het 4de element 0 is. Dat is dus al gedaan, de opdracht was dat alles wat nog gedaan moest worden 3 kwartier meer moest worden, maar nu is de taak die al af is ook 3 kwartier meer geworden. Ja ik weet het, dat voelt als een suf klein detail, maar dat is ook heel belangrijk bij programmeren, het moet precies doen wat het moet doen, rekening gehouden met elk klein minuut detail.
Oké, ben je er klaar voor? Dit keer moet jij het zelf bedenken. En vergeet niet dat je alles zo in je browser kunt proberen!
Opgave 1:
Wat het moet doen: De functie krijgt een woord. Geef van alle klinkers uit het woord de klinker terug die het laagst in het alfabet staat.
var a = function(woord) { //ga er vanuit dat er geen klinker in zit, en dat je die melding terug moet geven var klinker = 'zit geen klinker in'; //als er toch een klinker inzit verander de tekst die je terug moet geven in die klinker if (woord.indexOf('a') > -1) { klinker = 'a'; } if (woord.indexOf('e') > -1) { klinker = 'e'; } if (woord.indexOf('i') > -1) { klinker = 'i'; } if (woord.indexOf('o') > -1) { klinker = 'o'; } if (woord.indexOf('u') > -1) { klinker = 'u'; } return klinker; }
Opgave 2:
Wat het moet doen: De functie krijgt een getal, die moet met vijf vermenigvuldigd worden, vervolgens moet er 5 bij opgesteld worden, en als laatste gedeeld door 2 en dan terug gegeven.
De functie:
var b = function(Getal) { //keer 5 var getal = Getal * 5 //plus 5 Getal = getal + 5 //gedeeld door 2 getal = getal / 2 return getal; }
Opgave 3:
Wat het moet doen: De functie krijgt een zin en moet true terug geven als daar Orcado in voorkomt, en false als Orcado er niet in voorkomt.
De functie:
var c = function(zin) { if (zin.indexOf("orcado") >= 0) return true return false }
Opgave 4:
Wat het moet doen: De functie krijg een array en een element, en moet het element aan het einde van de array invoegen.
De functie:
var d = function(lijst, toevoeging) { lijst[lijst.length + 1] = toevoeging; return lijst; }
Opgave 5:
Wat moet het doen: De functie krijgt 2 getallen mee, de dag en de maand dat je jarig bent, en geeft terug over hoeveel dagen je jarig bent.
De functie:
var e = function(dag, maand) { //maar een nieuwe datum met de maand en dag en dit jaar var datum = new Date(maand + '-' + dag + '-' + new Date().getFullYear()); // trek daar de huidige datum vanaf en omdat je een resultaat krijg in milliseconden reken dat terug naar dagen return Math.ceil((datum - new Date()) / 1000 / 60 / 60 / 24); }
Opgave 6:
Wat het moet doen: De functie krijgt een array met getallen, als een getal een 4 is moet je daar het volgende getal in de rij bij optellen.
De functie:
var f = function(lijst) { for (var i = 0; i < lijst.length; i++) { //als een waarde in de lijst 4 is, tel de volgende waarde uit de lijst er bij op if (lijst[i] == 4) { lijst[i] += lijst[i + 1]; } } return lijst }
Opgave 7:
Wat het moet doen: De functie krijgt een positief heel getal en moet daarvan aangeven of het een priemgetal is, true voor als het een priemgetal is, false als het geen priemgetal is.
De functie:
var g = function(a) { //ga alle getallen af vanaf 2 (alles is deelbaar door 1) for (var i = 2; i <= a; i++) { //als het getal gedeeld door een ander getal geen rest waarde oplevert is het er door deelbaar en dus geen priemgetal if (a % i == 0) return false } return true }
Opgave 8:
Wat het moet doen: De functie krijgt 2 getallen mee, het eerste is het basisgetal, het tweede is het getal tot waar we het gaan verheffen. Het tweede getal is altijd een geheel getal, en je mag alleen de basis 4 wiskundige bewerkingen doen (plus, min, keer, delen).
De functie:
var h = function(basis, verheffer) { //als de verheffer 0 is, dan is het altijd 1 if (verheffer == 0) return 1 //werk volgens het idee 2^3 = 2*2^2 door de opgave heen tot de verheffer 0 is. if (verheffer < 0) return basis * h(basis, verheffer + 1) else return basis * h(basis, verheffer - 1) }
Opgave 9:
Wat moet het doen: De functie krijgt een array sorteert deze in oplopende volgorde en geeft de gesorteerde array terug.
De functie:
var k = function(lijst) { //maak een lege lijst, hier komt de gesorteerde lijst in te staan var gesorteerdelijst = []; //we houden een variabele bij om te controleren of we straks een hogere waarde hebben gevonden. var gevonden = false; //voor elke waarde in de oorspronkelijke lijst for (var i = 0; i < lijst.length; i++) { //blader door alle waardes in de gesorteerde rij tot je een waarde tegenkomt die groter is, zet daar de waarde uit de eerste lijst voor for (var j = 0; j < gesorteerdelijst.length; j++) { if (lijst[i] < gesorteerdelijst[j]) { gevonden = true; gesorteerdelijst.splice(j, 0, lijst[i]); j = lijst.length; //deze regel is goed, haal deze bij het testen niet weg, dan blijf je de waarde namelijk eindeloos in de nieuwe array invoegen, dat vindt je browser niet leuk } } //heb je geen grotere waarde gevonden, dan is dit de grootste waarde, zet die achteraan in de gesorteerde lijst. if (gevonden == false) { gesorteerdelijst.splice(lijst.length, 0, lijst[i]); } } return gesorteerdelijst }
Opgave 10:
Wat het moet doen: De functie krijgt een zin en moet de individuele woorden omdraaien en daarna aan elkaar plakken, bijvoorbeeld: “dit is een test zin” moet worden: “tidsineetsetniz”.
De functie:
var l = function(draaiom) { return draaiom.split("").reverse().join(" ").split(" ").reverse().join("") }
Viel dat nou erg mee? Dan is er voor jou wel een baan in de IT weggelegd. Viel dat nou niet mee? Vrees niet, code lezen en begrijpen wat het doet is iets wat je heel veel moet doen. En dan nog steeds is het soms lastig om zonder de code te draaien meteen te zien wat het doet, of wat het zou moeten doen. Was je vooral benieuwd naar Roelofs bot? Ik zou graag zeggen dat die er binnenkort echt aankomt, maar dat geldt alleen als je de wet van Hofstadter in acht neemt.