Programmeertalen en programmeerparadigma’s

Volg een programma van de code die iemand schrijft tot de binaire instructies die een processor uitvoert. Ontdek hoe compilers, interpreters, bytecode en just-in-timecompilers die vertaling verzorgen. Leer onderscheid te maken tussen de syntaxis van een taal en de betekenis ervan, hoe talen gegevenstypen controleren en waar een programma tijdens de uitvoering zijn gegevens bewaart en geheugen vrijgeeft. Vergelijk daarna vier manieren om programma’s te schrijven (imperatief, objectgeoriënteerd, functioneel en declaratief) door dezelfde problemen op elke manier op te lossen. Leer ook hoe je een taal kiest die bij een project past.

BeginnerLes 5 van 7 in Cursus Informatica: hoe computers, data, algoritmen en internet werken

Wat je leert

  1. Volg een programma van de code die iemand schrijft tot de binaire instructies die een processor uitvoert. Vergelijk de manieren waarop die vertaling kan plaatsvinden: vooraf, tijdens de uitvoering of als combinatie van beide.
    • Vergelijk machinecode, assembleertaal en hogere programmeertalen
      Beschrijf de drie niveaus waarop programma’s kunnen worden geschreven, laat zien hoe één regel code in een hogere programmeertaal overeenkomt met meerdere machine-instructies en leg uit waarom de meeste software op het hoogste niveau wordt geschreven.
    • Leg uit wat een compiler doet
      Beschrijf hoe een compiler een heel programma vooraf vertaalt, benoem de belangrijkste fasen en leg uit wat er gebeurt met fouten en met het voltooide uitvoerbare bestand.
    • Leg interpreters, bytecode en just-in-timecompilatie uit
      Beschrijf hoe een interpreter een programma uitvoert, hoe talen zoals Java en Python compileren en interpreteren combineren en hoe je de afwegingen tussen deze aanpakken maakt.
  2. Maak onderscheid tussen de regels voor hoe code moet worden geschreven en de regels voor wat code doet. Herken de bouwstenen die de meeste hogere programmeertalen delen en doorloop korte programma’s om hun resultaten te voorspellen.
    • Maak onderscheid tussen syntaxis en semantiek
      Leg het verschil uit tussen de vorm van een programma en de betekenis ervan, maak onderscheid tussen syntaxisfouten, uitvoeringsfouten en logische fouten en herken waar talen aan code die er hetzelfde uitziet een andere betekenis geven.
    • Doorloop de gemeenschappelijke bouwstenen van programma’s in hogere programmeertalen
      Herken de constructies die in bijna elke hogere programmeertaal voorkomen (variabelen, expressies, besturingsverloop en functies met hun bereik) en volg een kort programma stap voor stap om de uitvoer te voorspellen.
  3. Beschrijf hoe talen waarden naar type indelen en controleren, waar een programma tijdens de uitvoering zijn gegevens bewaart en hoe geheugen door de programmeur of automatisch wordt gereserveerd en vrijgegeven, met de afwegingen voor veiligheid en snelheid die elke keuze met zich meebrengt.
    • Vergelijk hoe programmeertalen gegevenstypen gebruiken en controleren
      Leg uit wat een gegevenstype is, vergelijk statische en dynamische typering en strikte en soepele conversieregels, en voorspel hoe veelgebruikte talen omgaan met gemengde typen en getalgrenzen.
    • Beschrijf waar een programma tijdens de uitvoering zijn gegevens bewaart
      Leg uit dat variabelen benoemde geheugenlocaties zijn, wat de rol is van de aanroepstapel en de heap en wat het verschil is tussen een waarde kopiëren en een referentie delen.
    • Vergelijk handmatig en automatisch geheugenbeheer
      Leg uit hoe geheugen wordt vrijgegeven in talen waarin de programmeur het beheert en in talen met automatische opruiming van onbereikbare objecten of eigenaarschapsregels. Breng geheugenfouten in verband met het vastlopen van programma’s en beveiligingslekken.
  4. Beschrijf programma’s eerst als reeksen opdrachten die een opgeslagen toestand veranderen en daarna als verzamelingen objecten die gegevens met gedrag bundelen. Gebruik klassen, inkapseling, overerving en polymorfisme om code te organiseren.
    • Beschrijf de imperatieve en procedurele programmeerstijl
      Leg imperatief programmeren uit als stapsgewijze opdrachten die de toestand van een programma veranderen, laat zien hoe procedures en een gestructureerd besturingsverloop die opdrachten organiseren en herken de risico’s van een gedeelde veranderlijke toestand.
    • Modelleer gegevens en gedrag met klassen en objecten
      Leg uit hoe objectgeoriënteerd programmeren gegevens en de bewerkingen daarop in objecten groepeert, lees en ontwerp een eenvoudige klasse en gebruik inkapseling om de gegevens van een object te beschermen.
    • Hergebruik en breid klassen uit met overerving, polymorfisme en compositie
      Gebruik overerving om gespecialiseerde klassen te bouwen, leg uit hoe polymorfisme één aanroep verschillend laat werken voor verschillende objecten en bepaal wanneer je objecten combineert in plaats van overerving te gebruiken.
  5. Bouw programma’s met functies die nieuwe waarden teruggeven in plaats van oude te veranderen, en schrijf programma’s die het gewenste resultaat beschrijven terwijl een taalengine bepaalt hoe dat wordt berekend.
    • Bouw programma’s met zuivere functies en onveranderlijke gegevens
      Leg zuivere functies, onveranderlijkheid, eersteklasfuncties, functies van hogere orde en recursie uit en herschrijf een lus als een verwerkingsketen van map, filter en reduce.
    • Beschrijf het gewenste resultaat met declaratieve talen
      Leg declaratief programmeren uit als het beschrijven van het gewenste resultaat in plaats van de manier om dat te bereiken, en lees voorbeelden in een databasequerytaal, een logische taal en andere declaratieve notaties.
  6. Los hetzelfde probleem op in verschillende stijlen, herken dat de meeste moderne talen paradigma’s mengen en weeg de technische en praktische factoren af die bepalen welke taal bij een project past.
    • Los hetzelfde probleem op volgens verschillende programmeerparadigma’s
      Schrijf dezelfde kleine taak in imperatieve, objectgeoriënteerde, functionele en declaratieve stijl, vergelijk wat elke versie gemakkelijk maakt en herken multiparadigmatalen.
    • Kies een programmeertaal voor een taak
      Weeg de factoren af die bepalen welke taal bij een project past (platform, domein, prestaties, veiligheid, ecosysteem en mensen) en onderbouw een keuze voor een concreet scenario.

Programmeertalen: uitleg

6 hoofdstukken

1

Van broncode naar uitvoerbare instructies

Een processor voert kleine binaire instructies uit, terwijl mensen programma’s meestal schrijven in talen die zijn opgebouwd rond het probleem dat ze willen oplossen. Vertaalprogramma’s verbinden deze twee niveaus. Volg dezelfde berekening door die lagen en vergelijk vervolgens het bouwen van een programma vóór de uitvoering met interpreteren of delen compileren tijdens de uitvoering. De route beïnvloedt de overdraagbaarheid, opstarttijd, snelheid en het moment waarop een fout kan verschijnen.

1.1 Drie niveaus van programmacode

Een regel zoals x = a + b beschrijft een berekening zonder een processorregister te noemen. Abstractie maakt dit mogelijk: een taal verbergt details van lagere niveaus, zodat een programmeur zich kan richten op het werk dat het programma moet doen.

Wat de processor uitvoert

Machinecode bestaat uit instructies die als binaire getallen zijn gecodeerd. De instructiesetarchitectuur (ISA) van een processor bepaalt de instructies en hun coderingen. x86-64 komt veel voor in pc’s en servers; ARM komt veel voor in telefoons, tablets en veel laptops. Code die voor één ISA is gebouwd, moet worden vertaald of geëmuleerd om op een andere te werken.

Met hexadecimale notatie kun je instructiebytes korter weergeven. Op x86-64 zet B8 01 00 00 00 de waarde 1 in register EAX. De bytes 01 D8 tellen de waarde in EBX op bij EAX. Registers zijn kleine opslaglocaties binnen de processor. Elke instructie voert een kleine handeling uit, zoals gegevens verplaatsen, optellen, vergelijken of springen. De instructiecyclus voert deze instructies uit.

Namen voor dezelfde kleine handelingen

Assembleertaal vervangt instructiebytes door leesbare namen, mnemonics genoemd. Je kunt die twee x86-64-instructies zo schrijven:

mov eax, 1
add eax, ebx

Een assembler zet assembleertaal om in machinecode. Een gewone instructieregel levert normaal gesproken één machine-instructie op. Registernamen en instructies horen nog steeds bij een bepaalde ISA, dus woorden gebruiken in plaats van bytes maakt assembleertaal niet overdraagbaar.

Beschrijf een probleem in minder regels

Een hogere programmeertaal biedt variabelen, expressies, voorwaarden, lussen en functies. Python, C, Java en JavaScript laten de programmeur bewerkingen beschrijven zonder registers te kiezen. Eén instructie vereist vaak meerdere machine-instructies. Een vereenvoudigde vertaling van x = a + b zou:

  1. De waarde van a in een register laden.
  2. De waarde van b in een ander register laden.
  3. De registerwaarden optellen.
  4. Het resultaat voor x opslaan.

Een echte compiler kan die stappen combineren of anders ordenen met behoud van het vereiste gedrag.

NiveauWat een programmeur schrijftWie verzorgt de hardwaredetails?
MachinecodeGecodeerde processorinstructiesDe programmeur of codegenerator
AssembleertaalMnemonics en registersDe programmeur, met een assembler die instructies codeert
Code in een hogere programmeertaalVariabelen en bewerkingen op de gegevens van het probleemEen compiler, interpreter of een combinatie

Broncode in een hogere programmeertaal is gemakkelijker te lezen, te veranderen en te controleren. Overdraagbare C-broncode kan eenmaal voor een x86-64-systeem en opnieuw voor een ARM-systeem worden gecompileerd. Elke build heeft nog steeds de juiste ondersteuning van het besturingssysteem en de juiste bibliotheken nodig. Overdraagbare broncode maakt een native uitvoerbaar bestand niet universeel.

Waarom de lagere niveaus nog steeds van belang zijn

Assembleertaal blijft nuttig in opstartcode, sommige routines van besturingssystemen en stuurprogramma’s, kleine ingebedde systemen en beveiligingsanalyse van programma’s zonder beschikbare broncode. Voor gewone toepassingscode produceren moderne compilers meestal machinecode met prestaties die vergelijkbaar zijn met die van met de hand geschreven assembleertaal.

De verschuiving naar hogere niveaus vond geleidelijk plaats. Assembleertaal verspreidde zich eind jaren 1940 en begin jaren 1950. Het A-0-systeem van Grace Hopper hielp in 1952 de automatische vertaling vooruit. IBM bracht FORTRAN uit in 1957; COBOL volgde in 1959 en Lisp ontstond rond 1958–1960. “Hoger” duidt de afstand tot de hardwaredetails aan, niet hoe moeilijk de taal te leren is.

Belangrijkste punten

  • Een processor voert machine-instructies uit die door zijn ISA zijn vastgelegd.
  • Een assembler codeert instructies in assembleertaal; assembleertaal blijft aan een ISA gebonden.
  • Instructies in een hogere programmeertaal verbergen hardwarestappen en worden vaak meerdere instructies.
  • Overdraagbare broncode kan voor verschillende doelen worden vertaald; native machinecode is doelspecifiek.
  • Code op een laag niveau blijft nuttig waar toegang tot of analyse van hardware dat vereist.

1.2 Hoe een compiler een programma bouwt

Een compiler vertaalt broncode naar doelcode. De doelcode kan native machinecode zijn of een tussenvorm zoals bytecode. Compilatie vooraf (ahead-of-time compilation, AOT) vindt plaats voordat het programma draait; just-in-timecompilatie vindt plaats tijdens de uitvoering.

Van tekst naar instructies

Bij een build vooraf zetten verschillende fasen een bronbestand om in instructies:

  1. Lexicale analyse splitst tekst op in tokens: betekenisvolle stukken zoals namen, operatoren en getallen.
  2. Syntactische analyse controleert de grammatica en bouwt een structuur die laat zien hoe de stukken in elkaar passen.
  3. Semantische analyse controleert betekenisregels, waaronder declaraties en toegestane combinaties van typen.
  4. Optimalisatie zoekt een efficiëntere manier om het vereiste werk uit te voeren.
  5. Codegeneratie produceert de doelinstructies.

Neem de C-instructie total = price * 3;. De zes tokens zijn total, =, price, *, 3 en ;. De namen zijn identifiers, 3 is een letterlijke waarde van een geheel getal en de puntkomma beëindigt de instructie. De syntactische analyse legt vast dat de vermenigvuldiging binnen de toekenning hoort: bereken price * 3 en sla de waarde daarna op in total.

Deze fasen hebben verschillende taken. Een correct gespelde reeks tokens kan nog steeds de grammatica schenden. Een grammaticaal correcte instructie kan nog steeds een type verkeerd gebruiken. Tokens vinden is niet genoeg om vast te stellen dat een programma geldig is.

Doe hetzelfde werk met minder middelen

Stel dat broncode seconds_per_day = 60 * 60 * 24 bevat. De factoren zijn constanten, dus de compiler kan tijdens de build 60 met 60 vermenigvuldigen om 3600 te krijgen en dat vervolgens met 24 vermenigvuldigen om 86400 te krijgen. Hij kan de eindwaarde in de uitvoer plaatsen in plaats van de vermenigvuldiging tijdens de uitvoering te herhalen. Dit is constantevaluatie tijdens compilatie.

Een optimalisatie moet het gedrag behouden dat de taal vereist. Ze mag een berekening niet vervangen door een snellere die andere vereiste resultaten geeft. Het doel is om werk, tijd of geheugen te besparen met behoud van de betekenis van het programma.

Voeg de onderdelen samen

Grote programma’s worden meestal uit veel bestanden opgebouwd. Een compiler kan voor elk bronbestand een objectbestand maken. Een objectbestand bevat vertaalde code die mogelijk nog verbindingen met andere code nodig heeft. Een linker voegt de onderdelen samen en lost aanroepen naar bibliotheekroutines op, zoals afdruk- of wiskundige functies.

LinkmethodeWaar de bibliotheekcode tijdens de uitvoering vandaan komt
Statisch linkenCode die tijdens de build in het uitvoerbare bestand is opgenomen
Dynamisch linkenEen compatibele gedeelde bibliotheek die bij het opstarten of tijdens de uitvoering wordt geladen

Het resulterende uitvoerbare bestand is gebouwd voor een doelplatform, inclusief een ISA en conventies van het besturingssysteem. De gebruiker heeft de compiler voor compilatie vooraf niet nodig om het uit te voeren. Gedeelde bibliotheken of uitvoeringscomponenten kunnen nog wel nodig zijn.

Begrijp wat een geslaagde build bewijst

In Java doorstaat int count = "five"; vóór de uitvoering een typecontrole niet. Tekst kan die variabele voor gehele getallen niet initialiseren. Het programma moet de vereiste controles tijdens de compilatie doorstaan voordat een uitvoerbare build beschikbaar is.

Andere fouten overleven de compilatie. Een deling door een door de gebruiker ingevoerde nul hangt af van gegevens tijdens de uitvoering. Een programma dat moet aftrekken maar in plaats daarvan optelt, kan draaien en de verkeerde waarde teruggeven. Compilatie controleert taalregels; tests moeten ook controleren of het resultaat bij de bedoelde taak past.

Belangrijkste punten

  • Compilatie vertaalt broncode naar doelcode, die geen machinecode hoeft te zijn.
  • Compilerfasen controleren tokens, structuur en betekenis voordat ze doelinstructies produceren.
  • Optimalisatie behoudt het vereiste gedrag; constantevaluatie berekent constanten tijdens de build.
  • Een linker verbindt objectbestanden en bibliotheekcode.
  • Een geslaagde build kan nog steeds uitvoeringsfouten of logische fouten bevatten.

1.3 Interpretatie, bytecode en JIT-compilatie

Een programma hoeft geen native uitvoerbaar bestand te worden voordat een gebruiker het start. Een interpreter voert het programma uit via een ander draaiend programma. Veel implementaties combineren interpretatie met compilatie, dus de nuttige vraag is hoe dit specifieke systeem code uitvoert.

Uitvoeren met een interpreter

Een interpreter moet beschikbaar zijn telkens wanneer die het programma uitvoert. Een interactieve REPL herhaalt vier handelingen: invoer lezen, evalueren, een resultaat afdrukken en terugkeren voor de volgende invoer. Dit geeft snel feedback wanneer je kleine expressies uitprobeert.

Interpreteren betekent niet noodzakelijk dat de ruwe broncode regel voor regel wordt gelezen. CPython, de standaardimplementatie van Python, compileert een bestand eerst naar bytecode. Een virtuele machine in software voert vervolgens die interne instructies uit. Bytecode hoort bij die uitvoeringsomgeving; het is niet de native instructieset van de processor.

Waarom sommige fouten eerder verschijnen

Vergelijk twee Python-bestanden. Dit bestand heeft geldige syntaxis:

print("start")
x = 10
print(x + "5")

Het drukt “start” af, koppelt x aan 10 en veroorzaakt vervolgens een TypeError. Het optellen van dat gehele getal bij een tekenreeks mislukt pas wanneer de expressie wordt uitgevoerd. Een vergelijkbare bewerking in een tak die niet wordt bereikt, kan onopgemerkt blijven totdat die tak wordt uitgevoerd.

Verwijder nu het sluithaakje uit de laatste regel. CPython kan het bestand niet compileren omdat de syntaxis onvolledig is. Het meldt een syntaxisfout voordat een instructie in dat bestand wordt uitgevoerd, dus “start” wordt niet afgedrukt. De syntaxiscontrole van het hele bestand en de controle tijdens de uitvoering van een bewerking vinden op verschillende momenten plaats.

Deel bytecode en specialiseer tijdens de uitvoering

Java laat een veelgebruikte hybride route zien. javac Hello.java produceert Hello.class, dat bytecode bevat. java Hello start een compatibele Java Virtual Machine (JVM). Verschillende JVM-implementaties laten die bytecode op verschillende processorfamilies draaien. De bytecode is overdraagbaar tussen compatibele uitvoeringsomgevingen; elke uitvoeringsomgeving moet zelf op het betreffende platform werken. C# richt zich op vergelijkbare wijze op een tussentaal die door .NET wordt gebruikt.

Een just-in-timecompiler (JIT-compiler) genereert machinecode terwijl het programma draait. De HotSpot-uitvoeringsomgeving van Java begint gewoonlijk met interpretatie en compileert veelgebruikte methoden. JavaScript-engines in browsers combineren ook uitvoeringstechnieken, waaronder JIT-compilatie. Veelgebruikte delen compileren kan de snelheid bij langdurige uitvoering verbeteren, maar kost tijd en geheugen tijdens de uitvoering.

RouteNuttig voordeelKosten om rekening mee te houden
Vooraf gebouwde native versieWerk is vóór de start vertaald; de gebruiker heeft geen compiler nodigAparte compatibele builds voor doelplatforms
InterpretatieSnelle feedback bij aanpassen en uitvoeren met een geïnstalleerde interpreterHerhaald werk tijdens de uitvoering kan rekenintensieve code vertragen
Bytecode met een VM en JITOverdraagbare tussencode met optimalisatie tijdens de uitvoeringInstallatie van de uitvoeringsomgeving, opwarmtijd en extra geheugengebruik

Een lus die één miljard waarden optelt, draait doorgaans veel sneller in gecompileerde C dan in CPython: CPython voert bij veel bewerkingen interpreterwerk en typecontroles uit. Voor een script dat 20 bestanden hernoemt, kan de ontwikkelinspanning veel belangrijker zijn dan dat verschil. Meet de werklast voordat je een taallabel als voorspelling van de snelheid gebruikt.

Gecompileerd en geïnterpreteerd beschrijven keuzes in de implementatie. CPython gebruikt bytecode-interpretatie; PyPy gebruikt JIT-technieken. Een taal kan meer dan één implementatie hebben en één implementatie kan verschillende uitvoeringsmethoden gebruiken.

Belangrijkste punten

  • Een interpreter voert een programma uit en moet aanwezig zijn om het te draaien.
  • CPython compileert een bestand naar bytecode voordat het die uitvoert.
  • Een syntaxisfout kan voorkomen dat een bestand start; een uitvoeringsfout verschijnt wanneer de betreffende bewerking wordt uitgevoerd.
  • Een JIT-compiler maakt tijdens de uitvoering native code, vaak voor veelgebruikte delen.
  • Uitvoeringsmethoden horen bij implementaties en de prestaties hangen af van de werklast.
2

Lees wat korte programma’s doen

Bij het lezen van code spelen twee vragen: volgt de vorm de regels van de taal en wat betekenen de bewerkingen? Een expressie die er hetzelfde uitziet, kan zich in twee talen anders gedragen. Leer fouten indelen en doorloop daarna toekenningen, beslissingen, lussen en functieaanroepen in de volgorde van uitvoering. Een kleine tabel met veranderende waarden maakt het verschil zichtbaar tussen wat een programmeur bedoelde en wat het programma daadwerkelijk doet.

2.1 Syntaxis, betekenis en drie soorten fouten

Syntaxis stelt de regels voor geldige programmatekst vast. Semantiek geeft die tekst betekenis. Een programma kan elke grammaticaregel volgen en toch het verkeerde resultaat berekenen, dus het controleren van de vorm is slechts het eerste deel van het lezen.

Herken de onderdelen en de grammatica

Tokens zijn onder meer sleutelwoorden zoals if, identifiers zoals total, letterlijke waarden zoals 3 of "hello", operatoren zoals + en ==, en leestekens. Een grammatica beschrijft hoe deze onderdelen expressies en instructies kunnen vormen.

Python vereist een dubbele punt na if x > 3: en gebruikt inspringing om de instructies te markeren die bij die voorwaarde horen. C, Java en JavaScript gebruiken accolades voor blokken; C en Java vereisen ook puntkomma’s na veel instructies. In die talen helpt inspringing de menselijke lezer, maar bepaalt die niet tot welk blok code behoort.

Verschillende syntaxis kan een vergelijkbare handeling uitdrukken. Deze instructies drukken een begroeting af:

print("hello")
printf("hello\n");
System.out.println("hello");

De C-escape \n voegt een nieuwe regel toe. Als je het idee van afdrukken kent, helpt dat bij het leren van de manier waarop elke taal dat uitdrukt.

Dezelfde symbolen kunnen verschillende dingen betekenen
Expressie en taalResultaatRegel
7 / 2 in Python of JavaScript3.5Deling levert een resultaat met een breukdeel op
7 / 2 met gehele getallen als operanden in C of Java3Gehele deling kapt af richting nul
-7 // 2 in Python-4Deling met afronding naar beneden rondt naar beneden af
-7 / 2 met gehele getallen in C-3Afkappen gaat richting nul
"3" * 4 in Python"3333"Een tekenreeks wordt herhaald
"3" * 4 in JavaScript12Tekst wordt voor de vermenigvuldiging omgezet in een getal

Dat een symbool er vertrouwd uitziet, bepaalt niet de betekenis ervan in verschillende talen.

Ook de evaluatievolgorde is van belang. Door de voorrang vinden vermenigvuldiging en deling vóór optelling plaats. Met a = 4 en b = 6 deelt average = a + b / 2 eerst 6 door 2 en telt daarna 4 op, wat 7,0 oplevert. De haakjes in average = (a + b) / 2 zorgen dat de optelling eerst gebeurt en geven 5,0.

Maak onderscheid tussen fouten

Een syntaxisfout schendt de grammatica, zoals een ontbrekende dubbele punt in Python. Een uitvoeringsfout ontstaat tijdens een uitgevoerde bewerking, zoals een deling door nul in Python. Een logische fout laat uitvoering toe maar geeft een onbedoeld antwoord, zoals bij het onjuiste gemiddelde.

Sommige geldige expressies slaan bewust een bewerking over. In Python gebruikt x != 0 and 10 / x > 2 kortsluitevaluatie. Wanneer x nul is, is de eerste voorwaarde onwaar, zodat de rechterkant niet wordt geëvalueerd en er geen deling plaatsvindt. Als je deze voorwaarden omdraait, valt die bescherming weg. Python schrijft and, or en not; C, Java en JavaScript gebruiken &&, || en !.

Toekenning en vergelijking vormen een ander belangrijk onderscheid. = slaat een waarde op; == test gelijkheid. C staat if (x = 5) toe: het kent 5 toe en beschouwt die waarde ongelijk aan nul als waar. Python wijst if x = 5: af als ongeldige syntaxis. Dezelfde fout kan daardoor in verschillende talen in een andere fase worden opgemerkt.

Belangrijkste punten

  • Syntaxis bepaalt de geldige vorm; semantiek bepaalt de betekenis.
  • Operatoren kunnen in verschillende talen een andere betekenis hebben.
  • Voorrang en haakjes bepalen welke bewerkingen worden gegroepeerd.
  • Syntaxisfouten, uitvoeringsfouten en logische fouten vereisen verschillende soorten controles.
  • Kortsluitevaluatie kan een onveilige bewerking overslaan.
  • Een toekenning slaat een waarde op; een gelijkheidsvergelijking vergelijkt waarden.

2.2 Volg waarden door het besturingsverloop en functieaanroepen

Een kort programma is gemakkelijker te begrijpen wanneer je telkens één uitgevoerde instructie volgt. Houd de huidige waarden zichtbaar in plaats van van de eerste regel naar een geschat resultaat te springen.

Expressies en toekenningen

Een expressie levert een waarde op: price * 3, x > 10 en len(name) zijn voorbeelden. Een instructie voert een handeling uit, zoals een waarde toekennen of een blok aansturen. In total = price * 3 wordt de expressie rechts geëvalueerd voordat het resultaat aan total wordt toegekend.

Een variabele is een naam die naar een waarde verwijst. Een toekenning geeft de naam zijn huidige waarde; het is geen wiskundige bewering dat beide kanten altijd gelijk zijn geweest. Als count de waarde 4 bevat, leest count = count + 1 eerst 4, telt er 1 bij op en koppelt count aan 5.

Volg de volgende instructie die wordt uitgevoerd

Drie structuren organiseren het besturingsverloop:

  • Een sequentie voert instructies op volgorde uit.
  • Selectie gebruikt een voorwaarde om een route te kiezen, zoals bij if en else.
  • Iteratie herhaalt een blok met een for- of while-lus.

De stelling van het gestructureerde programma, verbonden aan Böhm en Jacopini in 1966, laat zien dat deze structuren elk algoritme kunnen uitdrukken. Een for-lus doorloopt gewoonlijk de elementen van een verzameling. Een while-lus herhaalt zolang de voorwaarde waar blijft.

Bekijk deze Python-lus:

total = 0
for n in [3, 8, 5]:
    total = total + n

De ingesprongen toekenning wordt eenmaal voor elk element uitgevoerd. Een tracetabel legt de toestand na elke bijwerking vast:

MomentHuidige ntotal
Vóór de lusNog geen element0
Eerste bijwerking33
Tweede bijwerking811
Derde bijwerking516

Elk nieuw totaal gebruikt het vorige. Als je total binnen de lus zou terugzetten, zou dat een andere berekening opleveren omdat eerdere optellingen verloren zouden gaan.

Ga een functie binnen en verlaat die

Een functie groepeert een taak onder een naam. Een parameter is een naam in de definitie; een argument is een waarde die een aanroep doorgeeft:

def area(width, height):
    return width * height

result = area(3, 4)

De aanroep koppelt width aan 3 en height aan 4. De vermenigvuldiging levert 12 op en return stuurt die waarde naar de aanroeper. De uitvoering gaat verder bij de toekenning, zodat result de waarde 12 krijgt. De namen beschrijven één taak die met andere argumenten kan worden hergebruikt.

Een naam heeft een bereik

Het bereik bepaalt waar een naam kan worden gebruikt. Namen die binnen een functie worden aangemaakt, zijn gewoonlijk lokaal voor die aanroep. Een naam op het hoogste niveau is globaal binnen de module. Afzonderlijke aanroepen hebben afzonderlijke lokale koppelingen, ook wanneer ze dezelfde namen gebruiken.

count = 7

def reset():
    count = 0

reset()
print(count)

Dit drukt 7 af. De toekenning binnen reset maakt een lokale count aan; die vervangt de globale koppeling niet. Een functie kan in plaats daarvan een resultaat teruggeven en de aanroeper dat resultaat laten toekennen.

Bereik en levensduur beantwoorden verschillende vragen. Een lokale naam is niet beschikbaar voor code daarbuiten alleen omdat het object waarnaar die verwees nog bestaat. Teruggegeven objecten kunnen blijven bestaan en een teruggegeven geneste functie kan toegang houden tot variabelen uit de omsluitende aanroep; die bewaarde omgeving maakt deel uit van een closure.

Belangrijkste punten

  • Evalueer de expressie rechts voordat je de waarde van het doel van de toekenning bijwerkt.
  • Doorloop sequentie, selectie en iteratie in de volgorde waarin ze daadwerkelijk worden uitgevoerd.
  • Parameters zijn namen in een definitie; argumenten zijn doorgegeven waarden.
  • Een retourwaarde geeft een resultaat terug aan de aanroeper.
  • Lokale en globale namen kunnen afzonderlijke koppelingen hebben; bereik verschilt van levensduur.
3

Typen, referenties en geheugen

Waarden hebben een type, een plaats in het geheugen en een levensduur nodig. Deze keuzes verklaren waarom tekst die op een getal lijkt rekenkundige bewerkingen kan weigeren, waarom twee namen dezelfde veranderde lijst kunnen tonen en waarom ongebruikte gegevens soms geheugen bezet blijven houden. Vergelijk de controles die talen uitvoeren, volg een functieaanroep door het geheugen en onderzoek drie manieren om geheugen vrij te geven. Elke aanpak verandert de balans tussen controle, gemak en veiligheid.

3.1 Typen bepalen welke bewerkingen zinvol zijn

Een gegevenstype bepaalt mogelijke waarden en de bewerkingen die daarop worden ondersteund. Een geheel getal ondersteunt rekenen met gehele getallen; een booleaanse waarde stelt waar of onwaar voor. Tekenreeksen, lijsten, records en objecten combineren of ordenen andere waarden. Typen sturen ook hoe waarden in het geheugen worden weergegeven, hoewel de grootte van een waarde kan afhangen van de waarde en de implementatie.

Wanneer worden typen gecontroleerd?
AanpakWanneer controles plaatsvindenVoorbeeld
Statische typeringVóór de uitvoeringJava wijst int count = "five"; af
Dynamische typeringTijdens de uitvoeringPython controleert of een uitgevoerde bewerking bij de operanden past

In Python is count = 5 gevolgd door count = "five" geldig. De naam verwijst nu naar een tekenreeks. Het evalueren van count + 1 veroorzaakt dan een TypeError. Bij dynamische typering hebben waarden een bepaald type; typen verdwijnen niet.

Statische typering vereist niet dat bij elke variabele zichtbaar een type staat. Met type-inferentie kan een compiler een type afleiden uit de omringende code. Kotlin leidt voor val n = 5 een geheel getal af en Rust kan voor let n = 5; een geheel getal afleiden. De optionele typeaanduidingen van Python zijn anders: aparte hulpmiddelen zoals mypy kunnen ze controleren, maar de gewone uitvoering van Python dwingt ze niet af.

Wanneer worden waarden geconverteerd?

Het controlemoment en de striktheid van conversie zijn aparte vragen. Python wijst "3" + 4 af; expliciete conversie in int("3") + 4 geeft 7. JavaScript past voor verschillende operatoren verschillende regels voor automatische conversie toe:

  • "3" + 4 geeft de tekenreeks "34".
  • "3" * 4 geeft het getal 12.
  • "3" - 4 geeft het getal -1.

Automatische conversie heet impliciete typeconversie. De aanduidingen “sterke” en “zwakke” typering zijn informele manieren om de striktheid van conversie te bespreken, geen andere naam voor statische en dynamische typering. Python is dynamisch en strikt bij de gemengde optelling hierboven; C is statisch en staat toch veel impliciete numerieke conversies toe.

De functie input() van Python geeft een tekenreeks terug. Als iemand “30” intypt, heeft het resultaat nog steeds het type str. age + 1 mislukt, terwijl int(age) + 1 de waarde 31 geeft wanneer age die tekst bevat. Wat op het scherm verschijnt, is niet genoeg om het type te bepalen.

Getallen hebben grenzen aan hun representatie

Een geheel getal met teken van 32 bits geeft waarden van −2.147.483.648 tot en met 2.147.483.647 weer. De int van Java springt van het maximum naar het minimum wanneer er 1 bij wordt opgeteld. Overloop van gehele getallen met teken in C is ongedefinieerd gedrag, dus je mag in C niet uitgaan van dezelfde cyclische overloop. Gehele getallen in Python groeien zolang het geheugen dat toelaat: 2 ** 100 is precies 1.267.650.600.228.229.401.496.703.205.376.

Drijvendekommagetallen geven de meeste decimale breuken bij benadering binair weer. In Python en JavaScript levert 0.1 + 0.2 de waarde 0.30000000000000004 op. Een rechtstreekse vergelijking van dat resultaat met 0.3 geeft onwaar.

Als een berekening een kleine fout toelaat, vergelijk je waarden met een tolerantie die voor de taak is gekozen, bijvoorbeeld abs(a - b) < 1e-9. Zo’n kleine absolute tolerantie past slechts bij sommige grootordes. Gebruik voor exact decimaal boekhouden gehele kleinste eenheden of een geschikt decimaal type. De vergelijking veranderen kan een benaderende representatie niet exact maken.

Belangrijkste punten

  • Een type bepaalt waarden en ondersteunde bewerkingen.
  • Statische en dynamische typering beschrijven het controlemoment, terwijl impliciete typeconversie over conversieregels gaat.
  • Type-inferentie ondersteunt statische typering; typeaanduidingen in Python hebben aparte controlehulpmiddelen nodig.
  • Gehele getallen met een vaste bitbreedte kunnen overlopen, met gedrag dat afhangt van de taal.
  • Afronding van drijvendekommagetallen beïnvloedt gelijkheidstests en de keuze van getalrepresentatie.

3.2 Volg namen, objecten en functieaanroepen in het geheugen

Het hoofdgeheugen slaat de gegevens op van een programma dat draait. Elke byte heeft een adres. Talen bieden namen zodat programmeurs met waarden kunnen werken zonder voor elke bewerking ruwe adressen te schrijven.

Aanroepen gebruiken frames; objecten hebben een levensduur

In C neemt een int gewoonlijk 4 bytes in en haalt &x het adres van x op. Hogere programmeertalen verbergen adressen vaak achter referenties. Het belangrijke onderscheid is dat tussen een koppeling die een object benoemt en het object zelf.

De aanroepstapel houdt actieve functieaanroepen bij. Elke aanroep heeft een stapelframe met informatie zoals parameters, lokale koppelingen en waar de uitvoering na terugkeer moet doorgaan. Als main de functie area(3, 4) aanroept, wordt een frame voor area boven het frame van de aanroeper toegevoegd. De functie berekent 12, keert terug en het frame wordt verwijderd. Bij gewone geneste aanroepen keert de nieuwste aanroep het eerst terug.

Recursie voegt een aanroep toe voor elk nog niet afgerond niveau. Als aanroepen zich blijven opstapelen, kan de beschikbare stapelruimte opraken. Python veroorzaakt normaal gesproken een RecursionError na ongeveer 1.000 geneste aanroepen, waarbij de grens afhangt van de instellingen. Een stopvoorwaarde moet worden bereikt om een eindige recursieve berekening te laten eindigen.

De heap bevat dynamisch toegewezen objecten, zoals een lijst die groeit of een object dat langer bestaat dan de functie die het heeft gemaakt. Geheugentoewijzing reserveert opslagruimte. Een teruggegeven lijst kan op de heap blijven staan nadat de aanroep die de lijst maakte, is geëindigd.

Een referentie kopiëren deelt een object

Een referentie identificeert een object. Een pointer in C of C++ stelt expliciet een adres voor dat wordt gebruikt om toegang te krijgen tot gegevens. Het kopiëren van een referentie kopieert de toegang tot het object, niet alle gegevens van het object.

a = [1, 2]
b = a
b.append(3)

Na de eerste regel is er één lijst. De tweede regel koppelt er een andere naam aan. De laatste regel verandert die lijst, zodat zowel a als b [1, 2, 3] laat zien. Dit delen heet aliasing. Met b = a.copy() zou een afzonderlijke buitenste lijst ontstaan; bij deze lijst met gehele getallen zou toevoegen aan de nieuwe lijst a onveranderd laten.

Vergelijk toekenning met mutatie:

x = 5
y = x
y = y + 1

Het gehele getal 5 is onveranderlijk. De optelling levert 6 op en alleen y wordt opnieuw gekoppeld. x verwijst nog steeds naar 5. Getallen, tekenreeksen en tuples in Python zijn onveranderlijk; lijsten en woordenboeken zijn veranderlijk. De vaste inhoud van een tuple kan een referentie naar een veranderlijke lijst bevatten, dus onveranderlijkheid van de buitenste container bevriest niet elk genest object.

Pas dezelfde regels toe op argumenten

Wanneer Python een lijst aan een functie doorgeeft, verwijst de parameter naar hetzelfde object. Als je binnen de functie items.append(4) aanroept, verandert die lijst. De toekenning items = [] koppelt alleen de lokale parameternaam aan een nieuwe lijst. De naam van de aanroeper blijft aan het oorspronkelijke object gekoppeld.

C geeft argumentwaarden op waarde door. Om een functie de variabele van een aanroeper via haar adres te laten bijwerken, geef je een pointer daarnaar door. Stel bij het doorlopen in elke taal twee aparte vragen: “Naar welk object verwijst deze naam?” en “Verandert deze bewerking dat object of koppelt ze een naam aan iets anders?”

Belangrijkste punten

  • De aanroepstapel houdt onafgeronde aanroepen bij; de heap bevat dynamisch toegewezen objecten.
  • Een object kan langer bestaan dan de functie die het heeft gemaakt.
  • Een referentie kopiëren deelt een object; het dupliceert de gegevens niet.
  • Mutatie verandert een object, terwijl opnieuw koppelen verandert waarnaar een naam verwijst.
  • Bij het doorgeven van argumenten geldt hetzelfde onderscheid tussen gedeelde objecten en lokale koppelingen.

3.3 Geef geheugen vrij zonder gegevens die nog nodig zijn te verliezen

Toegewezen geheugen moet uiteindelijk opnieuw kunnen worden gebruikt. De uitdaging is gegevens na het laatste gebruik vrij te geven en tegelijk elk object te behouden dat het programma nog nodig heeft. Talen leggen deze verantwoordelijkheid bij de programmeur, een uitvoeringssysteem of door de compiler gecontroleerde eigenaarschapsregels.

Handmatig beheer en wat daarbij misgaat

In C vraagt malloc(n) om n bytes en geeft bij succes een pointer naar het blok terug. free(p) geeft een blok vrij dat door geheugentoewijzing is verkregen. De programmeur moet dat op het juiste moment en precies eenmaal doen. C++ ondersteunt ook handmatig beheer, hoewel de standaardhulpmiddelen vrijgave via de levensduur van objecten kunnen automatiseren.

Verschillende fouten hebben verschillende oorzaken:

FoutWat er misging
GeheugenlekNiet meer benodigde toegewezen opslag wordt niet vrijgegeven
Gebruik na vrijgaveEen pointer wordt gebruikt nadat de opslag waarnaar die wijst is vrijgegeven
Dubbele vrijgaveHetzelfde toegewezen blok wordt tweemaal vrijgegeven
BufferoverloopEen schrijfbewerking gaat voorbij de grenzen van een geheugenblok

Een pointer die naar vrijgegeven opslag blijft verwijzen, is verweesd. De opslag kan voor iets anders worden hergebruikt, dus toegang ertoe kan gegevens beschadigen of het programma laten vastlopen.

Voor een C-array int scores[10] zijn de geldige posities 0 tot en met 9. Schrijven naar scores[10] gaat voorbij de array en C biedt geen automatische grenscontrole. Python veroorzaakt een IndexError wanneer index 10 op een lijst met tien elementen wordt gebruikt, waardoor die toegang geen aangrenzend geheugen kan overschrijven.

Volg bereikbaarheid of tel referenties

Een garbage collector die bereikbaarheid volgt begint bij actieve wortels, zoals actieve referenties, volgt verbindingen tussen objecten en kan onbereikbare objecten opruimen. Java, C#, Go en JavaScript gebruiken methoden die bereikbaarheid volgen. Opruimen kost tijdens de uitvoering werk en geheugen; afhankelijk van de garbage collector kan het pauzes veroorzaken.

Referentietelling houdt referenties naar een object bij. Voor een nieuwe Python-lijst is de vereenvoudigde reeks: a = [1, 2] levert één referentie; b = a levert een tweede; del a verwijdert er één; b = None verwijdert de andere. CPython ruimt deze gewone lijst normaal gesproken op wanneer het aantal nul bereikt.

Een groep objecten kan elkaars aantallen boven nul houden, ook wanneer geen actief deel van het programma de groep kan bereiken. Dit is een referentiecyclus. CPython vult referentietelling aan met het opruimen van cycli. Referentietelling en het volgen van bereikbaarheid zijn beide technieken voor automatisch geheugenbeheer.

Controleer eigenaarschap vóór de uitvoering

Rust geeft elke waarde een eigenaar en controleert regels voor het lenen van referenties. Wanneer het eigenaarschap eindigt, geeft gegenereerde opruimcode de waarde vrij. Veilige Rust wijst gebruik af van referenties die langer zouden kunnen bestaan dan het geldige doel, zonder een garbage collector nodig te hebben die bereikbaarheid volgt. Swift gebruikt automatische referentietelling die door de compiler wordt ingevoegd.

Geheugenveiligheid is belangrijk om meer redenen dan vastlopende programma’s. Microsoft meldde in 2019 dat ongeveer 70% van de kwetsbaarheden waaraan het bedrijf jaarlijks CVE’s toekende, met geheugenveiligheid te maken had; Chromium meldde in 2020 een vergelijkbaar aandeel ernstige fouten. In december 2023 drongen CISA, NSA, FBI en internationale partners er bij softwaremakers op aan de overstap naar geheugenveilige talen te plannen.

Automatisch opruimen behoudt nog steeds bereikbare objecten. Een steeds groeiende globale lijst kan elk oud record in leven houden, ook nadat de applicatie het niet meer gebruikt. Een garbage collector kan niet bepalen dat deze bereikbare records voor de taak overbodig zijn geworden; het programma moet stoppen ze vast te houden.

Belangrijkste punten

  • Handmatige geheugentoewijzing vereist vrijgave op het juiste moment.
  • Geheugenlekken, gebruik na vrijgave, dubbele vrijgave en bufferoverloop hebben verschillende oorzaken.
  • Het volgen van bereikbaarheid vindt onbereikbare objecten; referentietelling reageert op verdwenen referenties.
  • CPython vult referentietelling aan met het opruimen van cycli.
  • Eigenaarschap kan vrijgave regelen zonder een garbage collector die bereikbaarheid volgt.
  • Automatisch opruimen kan geen objecten vrijmaken die het programma nog vasthoudt.
4

Organiseer een veranderende toestand en samenwerkende objecten

Een programma kan worden beschreven als opdrachten die waarden bijwerken. Een andere manier groepeert die waarden met de bewerkingen die ze beheersen. Deze zienswijzen werken vaak samen: een objectmethode kan een gewone lus bevatten. Volg eerst een veranderende toestand en bouw daarna een klein rekeningmodel om te zien hoe klassen regels beschermen en hoe overerving, polymorfisme en compositie verwante objecten een interface laten delen zonder verkeerde verantwoordelijkheden op zich te nemen.

4.1 Opdrachten, procedures en gedeelde toestand

Een programmeerparadigma is een manier om code te organiseren en over berekeningen na te denken. Bij imperatief programmeren geeft een programma opdrachten die zijn toestand veranderen: de waarden die variabelen en objecten op dat moment bevatten.

Maak de toestandsveranderingen zichtbaar

Een lopend totaal is een eenvoudige imperatieve berekening:

total = 0
for n in [3, 8, 5]:
    total = total + n

De begintoestand is total = 0. Door 3 op te tellen verandert die in 3. Door 8 op te tellen verandert die in 11. Door 5 op te tellen verandert die in 16. Elke toekenning gebruikt de toestand die de vorige stap achterliet, dus de volgorde maakt deel uit van de methode.

Deze stijl sluit aan bij de stapsgewijze veranderingen die de processor in registers en geheugen aanbrengt. De stijl past ook bij algoritmen die bewust een verzameling ter plekke bijwerken, zoals een sorteerroutine die elementen verwisselt.

Geef taken namen

Procedureel programmeren organiseert imperatieve code in functies of procedures. Een rapportageprogramma kan read_scores(), average(scores) en print_report(avg) aanroepen. Dankzij de namen kan een lezer de taak als geheel zien zonder meteen elke interne regel te volgen. Elke procedure kan afzonderlijk worden begrepen en getest.

C, Pascal, Fortran en COBOL zijn klassieke procedurele talen. Python ondersteunt deze stijl ook: een script kan benoemde functies gebruiken zonder klassen te definiëren.

Gestructureerd programmeren organiseert het besturingsverloop met sequentie, selectie, lussen en procedureaanroepen. Het vermijdt willekeurige sprongen die moeilijk te voorspellen maken welke instructie daarna wordt uitgevoerd. De brief “Go To Statement Considered Harmful” uit 1968 van Edsger Dijkstra hielp deze aanpak populair te maken. Gecontroleerde uitwegen zoals break, continue en return blijven nuttig: ze verlaten een bepaalde lusiteratie, lus of functie.

Effecten kunnen verder gaan dan een retourwaarde

Een neveneffect verandert iets buiten het teruggegeven resultaat van een functie. Afdrukken, een bestand opslaan, een netwerkbericht versturen en gedeelde gegevens veranderen zijn neveneffecten. Programma’s hebben zulke handelingen nodig om met de wereld te communiceren.

Problemen ontstaan wanneer niet-verwante code ongemerkt dezelfde toestand verandert. Stel dat apply_discount() een globale price met 10% verlaagt en add_shipping() er 5 bij optelt. Als je begint met 100, geeft eerst de korting toepassen 90 en daarna verzending toevoegen 95. Eerst verzending toevoegen geeft 105 en daarna de korting toepassen geeft 94,5. Beide procedures voeren hun eigen stappen correct uit, maar het gecombineerde resultaat hangt af van hun volgorde.

Door waarden door te geven en nieuwe resultaten terug te geven, kun je die volgorde zichtbaar maken:

price = add_shipping(apply_discount(100))

De binnenste aanroep wordt eerst uitgevoerd: korting, daarna verzending. Een andere nesting drukt een andere regel uit. Een duidelijke gegevensstroom helpt te zien of ook op de verzending korting moet worden gegeven.

OrganisatieBelangrijkste vraag tijdens het lezen
Imperatieve opdrachtenWelke waarde verandert hierna?
Benoemde proceduresWelke taak voert deze aanroep uit?
Gedeelde globale bijwerkingenWelke eerdere handeling veranderde deze waarde?

Imperatieve code komt nog steeds veel voor binnen moderne objectmethoden en gebeurtenisafhandelaars. Een breder objectgeoriënteerd of functioneel ontwerp kiezen, verhindert niet dat een klein onderdeel directe opdrachten gebruikt waar die stappen het algoritme duidelijk maken.

Belangrijkste punten

  • Imperatieve code beschrijft stappen die de opgeslagen toestand veranderen.
  • Procedures geven samenhangende taken namen en grenzen.
  • Een gestructureerd besturingsverloop maakt uitvoeringsroutes gemakkelijker te volgen.
  • Neveneffecten omvatten veranderingen in gedeelde gegevens en interacties met de buitenwereld.
  • Door gedeelde bijwerkingen kunnen resultaten afhangen van de aanroepvolgorde.

4.2 Klassen houden gegevens bij hun regels

Objectgeoriënteerd programmeren (OOP) modelleert een programma als samenwerkende objecten. Elk object combineert gegevens met bewerkingen op die gegevens. Een rekening kan de eigenaar en het saldo bevatten en tegelijk bewerkingen voor storten en opnemen aanbieden.

Maak onderscheid tussen de blauwdruk en de instanties

Een klasse definieert de attributen en methoden die haar objecten bieden. Een object, of instantie, is één specifiek exemplaar van die klasse. BankAccount is de blauwdruk; de rekening van Ana met 150 en de rekening van Ben met 40 zijn afzonderlijke instanties. Hun saldi kunnen onafhankelijk van elkaar veranderen.

Een attribuut slaat gegevens op die bij een object horen. Een methode is een bewerking die via een object wordt aangeroepen, zoals acct.deposit(50). self in een Python-instantiemethode verwijst naar het object dat de aanroep ontvangt.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be positive")
        self._balance = self._balance + amount

De aanroep BankAccount("Ana", 100) construeert een nieuwe instantie. De methode __init__ initialiseert de gegevens van die instantie. Een constructor is het proces dat een nieuw object klaar voor gebruik maakt; Python scheidt het maken van deze initialisatiestap. De aanroep acct.deposit(50) op die rekening verandert het saldo van 100 naar 150.

Plaats de regel waar de bijwerking gebeurt

Inkapseling brengt gegevens en de bewerkingen die de regels ervoor bewaken samen. Stel dat een rekening nooit een negatief saldo mag hebben. De opnamemethode controleert de voorgestelde verandering voordat die wordt uitgevoerd:

def withdraw(self, amount):
    if amount <= 0:
        raise ValueError("amount must be positive")
    if amount > self._balance:
        raise ValueError("insufficient funds")
    self._balance = self._balance - amount

Deze methode hoort binnen BankAccount. Bij een saldo van 150 veroorzaakt withdraw(200) een fout en blijft 150 over. withdraw(30) slaagt en laat 120 over. De mislukte aanroep trekt niet eerst af om daarna het probleem te ontdekken; de controle vindt vóór de bijwerking plaats.

Andere code hoort die methoden te gebruiken in plaats van een ongecontroleerd saldo toe te kennen. Java en C# kunnen directe toegang beperken met private. De voorloopunderscore in _balance van Python geeft volgens afspraak een intern detail aan; die dwingt geen afscherming af.

Informatieverberging gaat verder: aanroepers zijn afhankelijk van de bewerkingen die een klasse aanbiedt zonder de interne representatie te hoeven kennen. Het saldo zou later anders kunnen worden opgeslagen terwijl de openbare stortings- en opnamebewerkingen hun betekenis behouden.

Laat het probleem verantwoordelijkheden suggereren

Een bibliotheeksysteem heeft boeken, leden en uitleningen. Die zelfstandige naamwoorden suggereren mogelijke klassen of attributen; handelingen zoals lenen en verlengen suggereren methoden. Kies voor elke klasse één duidelijke verantwoordelijkheid. Een rekeningklasse die ook elke e-mail verstuurt en elk rapport opmaakt, wordt moeilijker te veranderen omdat niet-verwante taken worden gemengd.

Klassen en objecten verschenen in Simula 67 in 1967; Smalltalk hielp de aanpak in de jaren 1970 te verspreiden. Java, C#, C++ en Python ondersteunen OOP, maar het gebruik van een van die talen dwingt niet elk programma in klassen. De nuttige ontwerpvraag is welke gegevens en regels bij elkaar horen.

Belangrijkste punten

  • Een klasse definieert attributen en methoden; elke instantie heeft eigen instantiegegevens.
  • Methoden werken op het object dat de aanroep ontvangt.
  • Inkapseling brengt validatie en toestandsveranderingen samen.
  • Underscores in Python duiden volgens afspraak interne attributen aan; sommige talen dwingen afgeschermde toegang af.
  • Door informatieverberging kunnen aanroepers afhangen van de openbare bewerkingen van een klasse in plaats van de interne werking.
  • Een klasse hoort een samenhangende verantwoordelijkheid te hebben.

4.3 Specialiseer gedrag en combineer objecten

Objecten moeten vaak bewerkingen delen terwijl ze in details verschillen. Overerving, polymorfisme en compositie lossen verschillende delen van dat ontwerpprobleem op.

Specialiseer een bestaande soort object

Overerving definieert een subklasse vanuit een superklasse. In Python geeft class SavingsAccount(BankAccount): de gespecialiseerde klasse toegang tot geërfd rekeninggedrag. Ze kan add_interest(rate) toevoegen: een saldo van 1000 wordt met 2% rente 1020.

De bedoelde relatie is is-een. Een spaarrekening is nog steeds een bankrekening en moet voldoen aan de regels die voor bankrekeningen worden verwacht. Een paar regels hergebruiken is niet genoeg reden om te stellen dat de ene soort object een andere is.

Een subklasse kan een methode overschrijven met een eigen implementatie. Stel dat een betaalrekening 1 aan kosten rekent voor elke geslaagde opname. Bij een saldo van 100 kost een opname van 30 dan 31 en blijft 69 over. Om de rekeningregel te behouden, moet de methode de opname plus de kosten controleren voordat het saldo verandert. Als die gecombineerde kosten hoger zijn dan het saldo, weigert de methode de hele bewerking.

Laat één aanroep met verschillende objecten werken

Polymorfisme laat dezelfde bewerking op verschillende objecttypen werken. Een cirkel en een rechthoek kunnen beide area() aanbieden:

for shape in shapes:
    print(shape.area())

De lus heeft geen if nodig voor elke vormklasse. Elk object levert zijn eigen implementatie. Het selecteren van die implementatie tijdens de uitvoering heet dynamische methodekeuze.

Voor Circle(2) is de oppervlakte ongeveer 12,57 vierkante eenheden. Voor Rectangle(3, 4) is die 12 vierkante eenheden. Hun gezamenlijke oppervlakte is ongeveer 24,57 vierkante eenheden. Een latere Triangle kan aan de verzameling worden toegevoegd door een compatibele bewerking area() aan te bieden; de lus zelf heeft geen extra tak nodig.

Een interface geeft aan welke bewerkingen een type moet aanbieden. De Java-interface interface Shape { double area(); } vereist dat implementerende klassen de oppervlaktemethode leveren. Een abstracte klasse kan een deel van de gedeelde implementatie bieden en andere methoden overlaten aan subklassen. Python gebruikt vaak ducktyping: een object kan in de lus werken als het de vereiste bewerking biedt, ook zonder een gedeelde superklasse.

Bevat een onderdeel in plaats van het te worden

Compositie slaat andere objecten op als onderdelen. Een auto heeft een motor en wielen. De auto kan de methode start() van de motor aanroepen zonder van Engine te erven.

RelatieGeschikt ontwerpReden
Een spaarrekening is een rekeningOverervingDie behoudt het verwachte gedrag van de rekening
Een auto heeft een motorCompositieDe motor is een onderdeel, niet de categorie van de auto
Verschillende vormen bieden oppervlakteGedeelde bewerking of interfaceDe aanroeper kan de bewerking op dezelfde manier aanvragen

Diepe overervingsketens kunnen de gevolgen van een verandering in een superklasse door veel klassen verspreiden. Een subklasse die geërfd gedrag moet uitschakelen, kan wijzen op een slechte is-eenrelatie. Compositie houdt zulke afhankelijkheden vaak kleiner en duidelijker.

De vier veelgenoemde pijlers van OOP passen hier samen: inkapseling beschermt gegevens via bewerkingen, abstractie verbergt implementatiedetails, overerving specialiseert een type en polymorfisme laat een aanroeper een gedeelde bewerking op verschillende implementaties gebruiken.

Belangrijkste punten

  • Overerving modelleert een is-eenrelatie en moet het verwachte gedrag behouden.
  • Methodeoverschrijving vervangt een methode-implementatie in een gespecialiseerde klasse.
  • Polymorfisme laat één bewerking verschillende implementaties aanroepen.
  • Een interface geeft vereiste bewerkingen aan; ducktyping vertrouwt op de bewerkingen die een object biedt.
  • Compositie modelleert objecten die andere objecten als onderdelen hebben.
5

Bereken nieuwe waarden en beschrijf gewenste resultaten

Een programma kan zich richten op transformaties van waarden of op het resultaat dat een engine moet produceren. Zuivere functies maken afhankelijkheden expliciet, terwijl onveranderlijke gegevens veranderingen via gedeelde referenties voorkomen. Declaratieve query’s laten een gespecialiseerde engine uitvoeringsstappen kiezen. Volg een gegevensverwerkingsketen en een databasequery om te zien wat deze aanpakken gemakkelijker maken en waar invoer, uitvoer en concrete machine-instructies nog steeds een rol spelen.

5.1 Zuivere functies en gegevensverwerkingsketens

Functioneel programmeren bouwt resultaten op door functies te combineren. De centrale werkwijze is nieuwe waarden uit invoer produceren en tegelijk gedeelde mutatie beperken. Lisp introduceerde rond 1958 veelgebruikte functionele ideeën; Haskell, Erlang, Clojure, F# en OCaml ontwikkelen die ideeën op verschillende manieren verder.

Weet wat een functie zuiver maakt

Een zuivere functie geeft hetzelfde resultaat voor dezelfde argumenten en heeft geen neveneffecten. def with_tax(price): return price * 1.2 illustreert het idee: with_tax(50) geeft 60.0. Een functie die ook iets aan een globale lijst toevoegt of een bericht afdrukt, heeft een effect naast de teruggegeven waarde. Een resultaat dat afhangt van de huidige tijd hangt ook van meer af dan de argumenten.

Een zuivere aanroep kan worden vervangen door de teruggegeven waarde zonder het gedrag van het programma te veranderen. Deze eigenschap heet referentiële transparantie. Ze maakt afzonderlijke tests eenvoudig en laat onafhankelijke aanroepen parallel draaien zonder te concurreren om een gedeelde toestand bij te werken.

Onveranderlijkheid houdt een bestaande waarde ongewijzigd. [n + 1 for n in nums] maakt een nieuwe lijst en laat nums intact. Een programma kan veranderende informatie nog steeds weergeven door nieuwe versies te maken; het hoeft de oude versie niet te overschrijven.

Geef gedrag als gegevens door

Functies zijn eersteklasfuncties wanneer ze aan namen kunnen worden toegekend, als argument kunnen worden doorgegeven en als waarden kunnen worden teruggegeven. Een functie van hogere orde ontvangt een functie of geeft er een terug. Een lambda-expressie is een naamloze functie-expressie, zoals lambda n: n * n.

Drie veelvoorkomende bewerkingen van hogere orde vormen een verwerkingsketen:

  • filter behoudt elementen die aan een test voldoen.
  • map transformeert elk overgebleven element.
  • reduce combineert elementen met een accumulator tot één resultaat.

Om de kwadraten van de even getallen van 1 tot en met 6 op te tellen, blijven na het filteren 2, 4 en 6 over; kwadrateren geeft 4, 16 en 36; optellen geeft 56.

sum(map(lambda n: n * n,
        filter(lambda n: n % 2 == 0, range(1, 7))))

Hier controleert % 2 == 0 of een getal even is en levert range(1, 7) 1 tot en met 6. Haskell drukt dezelfde berekening uit als sum (map (^2) (filter even [1..6])).

Met reduce, geïmporteerd uit functools van Python, combineert reduce(lambda acc, n: acc + n, [3, 8, 5], 0) eerst 0 met 3 tot 3, dan met 8 tot 11 en dan met 5 tot 16. De verwerkingsketen beschrijft transformaties zonder een omringende lus die een gedeeld totaal bijwerkt.

Bewaar een omgeving of roep een kleiner probleem aan

def multiplier(k): return lambda x: k * x geeft een functie terug die toegang tot k behoudt. Daardoor geeft multiplier(3)(10) de waarde 30 terug, ook nadat de buitenste aanroep is afgerond. De teruggegeven functie en de bewaarde omgeving vormen een closure.

Bij recursie roept een functie zichzelf aan op een kleiner probleem. De faculteitsfunctie geeft 1 terug bij n == 0; anders geeft ze n * factorial(n - 1) terug. Voor 5 geven de vermenigvuldigingen 120. Een eindige berekening heeft een bereikbaar basisgeval nodig. Niet-afgeronde geneste aanroepen in Python veroorzaken uiteindelijk een RecursionError; talen met volledige ondersteuning voor staartaanroepen kunnen stapelruimte hergebruiken voor aanroepen op de laatste positie.

Alleen functies gebruiken maakt code niet functioneel: procedurele functies kunnen de toestand veranderen. Echte functionele programma’s lezen nog steeds invoer en schrijven uitvoer, waarbij ze effecten gewoonlijk aan de randen van het programma houden en de centrale berekeningen zuiver.

Belangrijkste punten

  • Zuivere functies hangen alleen af van hun argumenten en hebben geen neveneffecten.
  • Bijwerken met onveranderlijke gegevens maakt nieuwe waarden.
  • Eersteklasfuncties kunnen worden doorgegeven en teruggegeven; functies van hogere orde ontvangen of retourneren functies.
  • Filter selecteert, map transformeert en reduce combineert.
  • Een closure bewaart de omgeving waarin die is aangemaakt.
  • Een eindige recursieve berekening heeft een basisgeval nodig dat daadwerkelijk wordt bereikt.

5.2 Beschrijf een resultaat en laat een engine het vinden

Declaratief programmeren beschrijft het gewenste resultaat en laat de uitvoeringsstappen over aan een taalengine. Functionele code wordt gewoonlijk onder dit brede idee geschaard, naast querytalen en logisch programmeren.

Lees een query als een verzoek

Een relationele database slaat gegevens op in tabellen. Stel dat students de rijen met naam en cijfer Ana 92, Ben 75, Chen 80 en Dara 64 bevat:

SELECT name
FROM students
WHERE grade >= 80
ORDER BY name;

FROM noemt de tabel, WHERE geeft de voorwaarde, SELECT kiest het teruggegeven veld en ORDER BY vraagt om alfabetische volgorde. Ana en Chen voldoen aan de voorwaarde, ook met Chens cijfer van precies 80. Het resultaat bestaat uit hun namen in die volgorde.

Een imperatief programma zou een resultatenlijst maken, rijen doorlopen, elk cijfer testen, namen die voldoen toevoegen en de lijst sorteren. SQL geeft de database het vereiste resultaat zonder die lus uit te schrijven.

Een queryoptimizer kiest een uitvoeringsplan. Die kan de tabel doorlopen of een beschikbare index gebruiken, een opzoekstructuur die geschikte zoekopdrachten kan versnellen. Veel indexen gebruiken gesorteerde structuren; andere gebruiken een andere ordening. Een index toevoegen kan het plan veranderen zonder de query te veranderen. Meer gegevens betekenen nog steeds werk: het resultaat declareren garandeert niet dat elke query weinig werk kost.

Vraag wat uit feiten en regels volgt

Logisch programmeren beschrijft feiten en relaties en zoekt vervolgens naar waarden die aan een vraag voldoen. In Prolog:

parent(tom, bob).
parent(bob, ann).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).

De regel betekent dat X een grootouder van Z is wanneer er een Y bestaat waarvoor beide ouderrelaties gelden. De query ?- grandparent(tom, Who). vindt Who = ann: Tom is een ouder van Bob en Bob is een ouder van Ann. Namen met een hoofdletter staan voor variabelen; de namen in kleine letters duiden specifieke personen aan.

Herken declaratieve hulpmiddelen om je heen

Een spreadsheetformule =SUM(B2:B5) beschrijft een afhankelijkheid. Het spreadsheetprogramma berekent die opnieuw wanneer relevante cellen veranderen. De reguliere expressie [0-9]{3}-[0-9]{4} beschrijft drie cijfers, een koppelteken en vier cijfers, zoals in “555-0199”. Een tekstzoekengine voert de zoekopdracht uit.

CSS beschrijft het uiterlijk met een regel zoals h1 { color: navy; }; HTML markeert de structuur van een pagina. Het zijn declaratieve notaties die gewoonlijk als stijl- en opmaaktalen worden ingedeeld. Hun rol verschilt van die van een taal voor algemene doeleinden die een breed scala aan berekeningen kan uitdrukken.

Soort taalGericht opVoorbeelden
Domeinspecifieke taalEen bepaalde soort taakSQL, reguliere expressies, CSS
Taal voor algemene doeleindenVeel soorten programma’sPython, Java, Haskell

Declaratieve code kan dicht bij een probleemstelling blijven en een engine de methode laten verbeteren. Ze geeft de programmeur ook minder directe controle over afzonderlijke stappen, wat het moeilijker kan maken de oorzaak van een traag plan vast te stellen. Daaronder worden nog steeds concrete instructies uitgevoerd: declaratief beschrijft hoe de programmeur de taak uitdrukt, niet een processor die zonder stappen werkt.

Belangrijkste punten

  • Declaratieve code beschrijft een gewenst resultaat; een engine kiest de uitvoeringsstappen.
  • SQL selecteert rijen en velden met voorwaarden en een gevraagde volgorde.
  • Een optimizer kan een queryplan veranderen zonder de query te veranderen.
  • Logisch programmeren leidt antwoorden af uit feiten en regels.
  • Domeinspecifieke talen richten zich op bepaalde taken; talen voor algemene doeleinden ondersteunen veel soorten programma’s.
6

Kies een stijl en een taal voor de taak

Verschillende programmeerstijlen kunnen hetzelfde antwoord opleveren terwijl ze het werk anders organiseren. Door ze bij één taak te vergelijken, worden hun afwegingen duidelijker. Bij de keuze van een taal komen vervolgens praktische randvoorwaarden: het doelplatform, beschikbare bibliotheken, geheugen- en snelheidsgrenzen, veiligheidseisen en de mensen die de code onderhouden. Een project met verschillende onderdelen kan voor elk onderdeel een andere taal en een andere combinatie van stijlen gebruiken.

6.1 Eén taak in verschillende programmeerstijlen

Een paradigma verandert hoe een oplossing wordt uitgedrukt, niet welk numeriek antwoord een correcte oplossing moet opleveren. Neem bestellingen met bedragen [120, 45, 300, 80]: bepaal het totaal van de bedragen die strikt groter zijn dan 100.

Volg veranderingen in de toestand

De imperatieve versie toont de lus en accumulator:

total = 0
for amount in amounts:
    if amount > 100:
        total = total + amount

Het eerste bedrag verandert total in 120. Het tweede laat de waarde onveranderd. Het derde verandert de waarde in 420 en het vierde laat die daar staan. Het programma maakt de volgorde van de test en de bijwerking zichtbaar.

Transformeer een verzameling

Een compacte Python-expressie in functionele stijl is sum(a for a in amounts if a > 100). Die selecteert 120 en 300 en combineert ze tot 420. Een andere versie is sum(filter(lambda a: a > 100, amounts)). Hier beschrijft het predicaat welke waarden erbij horen en combineert sum de geselecteerde waarden.

Een generatorexpressie of filter-aanroep gebruikt de mogelijkheden van Python voor meerdere paradigma’s; de aanwezigheid ervan maakt niet elk omringend deel van de applicatie zuiver.

Plaats regels bij objecten

In een objectgeoriënteerd ontwerp slaat een Order een amount op en biedt die is_large() aan, dat self.amount > 100 teruggeeft. Een OrderBook bevat bestelobjecten en biedt large_total() aan. Die methode kan bestellingen doorlopen, bij elk object vragen of het voldoet en de bedragen die voldoen optellen.

Het totaal blijft 420. Het verschil zit in waar de verantwoordelijkheden liggen: de regel voor een grote bestelling hoort bij Order en het werk op verzamelingsniveau hoort bij OrderBook. Andere functionaliteiten kunnen de regel hergebruiken zonder de drempeltest te kopiëren.

Vraag het de database

Als de gegevens al in een relationele database staan, kan SQL het totaal opvragen:

SELECT SUM(amount)
FROM orders
WHERE amount > 100;

De database kiest het plan voor ophalen en aggregeren. De query geeft een aanroeper geen opdracht om elke rij te bezoeken of een lokale accumulator bij te houden.

StijlWat het voorbeeld benadrukt
ImperatiefTests en bijwerkingen van een totaal in volgorde
ObjectgeoriënteerdBestellingen en verzamelingsobjecten met vastgelegde verantwoordelijkheden
FunctioneelSelectie en combinatie van waarden
Declaratieve SQLHet vereiste resultaat over opgeslagen gegevens
Combineer stijlen waar ze helpen

Python ondersteunt procedurele, objectgeoriënteerde en functionele technieken. JavaScript biedt objecten, eersteklasfuncties en closures. C++, C#, Kotlin, Scala, Swift en Rust mengen ook stijlen. Java wordt sterk met OOP geassocieerd maar ondersteunt sinds Java 8 in 2014 lambda-expressies; Haskell is zuiver functioneel.

Een webapplicatie kan klassen voor gebruikers en bestellingen gebruiken, functionele transformaties voor rapportgegevens, imperatieve bijwerkingen binnen methoden en SQL voor databasequery’s. Deze keuzes werken op verschillende lagen.

Gebeurtenisgestuurd programmeren voegt een ander organisatieprincipe toe: registreer een afhandelaar die wordt uitgevoerd wanneer een gebeurtenis binnenkomt. In JavaScript geeft button.addEventListener("click", showMenu) de functie showMenu als waarde door. Een latere klik roept die aan. De afhandelaar kan zelf objecten, zuivere transformaties of imperatieve stappen gebruiken. De gebeurtenisgestuurde rol zegt wanneer de afhandelaar draait, niet dat de inhoud maar één stijl mag gebruiken.

Belangrijkste punten

  • Correcte oplossingen in verschillende paradigma’s kunnen hetzelfde resultaat berekenen.
  • Een stijl verandert waar stappen, toestand, regels en verantwoordelijkheden voorkomen.
  • Moderne talen ondersteunen gewoonlijk meerdere paradigma’s.
  • Een gebeurtenisafhandelaar draait als reactie op een gebeurtenis en kan intern andere stijlen gebruiken.

6.2 Kies op basis van randvoorwaarden en afwegingen

Geen enkele programmeertaal past het best bij elk project. Talen voor algemene doeleinden zijn doorgaans turingvolledig: met onbeperkte tijd en geheugen kunnen ze dezelfde klasse berekeningen uitdrukken. Echte keuzes hangen af van de vraag of een oplossing past bij het daadwerkelijke platform, de middelen, de hulpmiddelen en de mensen.

Begin met wat moet werken

Het platform is vaak een harde randvoorwaarde. Browsers voeren JavaScript uit en kunnen gecompileerde modules via WebAssembly uitvoeren. TypeScript voegt statische controles toe en compileert naar JavaScript. Swift is een gebruikelijke keuze voor apps op Apple-platforms; Kotlin wordt aanbevolen voor Android. SQL is de gebruikelijke taal voor relationele query’s.

Kijk daarna naar het domein en het ecosysteem. Python heeft veelgebruikte bibliotheken voor gegevens en machinelearning, zoals NumPy en pandas. R richt zich op statistiek. C en C++ komen veel voor in besturingssystemen, game-engines en code voor ingebedde systemen. Java en C# komen veel voor in grote bedrijfssystemen; Bash is nuttig voor het automatiseren van taken van het besturingssysteem.

Dit zijn praktische verbanden, geen exclusieve toepassingsgebieden. Een geschikte bibliotheek kan veel meer ontwikkelwerk besparen dan een klein verschil in de syntaxis van talen.

Onderzoek de behoeften aan middelen en veiligheid

Een microcontroller met slechts enkele kilobytes RAM heeft mogelijk nauwkeurige controle over het geheugen en de overhead tijdens de uitvoering nodig. C, C++ of Rust kan geschikt zijn waar het doelplatform die talen ondersteunt. Ook voor veeleisende onderdelen van games of databases gaat de voorkeur vaak uit naar die talen, maar engines en beschikbare middelen verschillen: Unity gebruikt C# en sommige databases gebruiken Java of Go.

Zoek voordat je op snelheid kiest het knelpunt: het onderdeel dat de prestaties beperkt. Een programma dat vooral op een netwerkantwoord wacht, wordt niet veel sneller alleen omdat een kleine lokale lus efficiënter wordt gecompileerd. Meet representatief werk op het doelplatform.

Geheugenveiligheid vermindert fouten zoals ongeldige geheugentoegang. Veilige Rust controleert eigenaarschapsregels tijdens de compilatie; beheerde talen zoals Java, C#, Go en Python bieden geheugenbeheer tijdens de uitvoering. Statische typering vangt veel incompatibele bewerkingen vóór de uitvoering op, wat in een grote codebase kan helpen. Noch geheugenveiligheid noch statische typering bewijst dat de logica van het programma correct is.

Neem de kosten voor onderhoud van het resultaat mee

De kennis van het team, beschikbare ontwikkelaars, debuggers, testhulpmiddelen, bibliotheken, licenties en bestaande code beïnvloeden allemaal de beslissing. Een vertrouwde, ondersteunde taal kan de kosten voor oplevering en onderhoud verlagen. Een werkend systeem herschrijven brengt migratiewerk en risico’s mee, ook als de nieuwe taal aantrekkelijke eigenschappen heeft.

Voor een stedelijk weernetwerk hebben verschillende onderdelen verschillende behoeften:

OnderdeelRedelijke taalkeuzeReden
Firmware voor kleine sensorenC of RustControle over geheugen en ondersteunde hardwaredoelen
Opgeslagen metingenSQLFilteren en aggregeren in de database
Opschonen en analyserenPythonBibliotheken voor gegevensverwerking
Interactieve weergave in de browserJavaScript of TypeScriptBrowserondersteuning en hulpmiddelen voor interfaces

Een nuttige onderbouwing verbindt elke keuze aan een vereiste: “Het team onderhoudt de Java-service al en de bibliotheken ondersteunen de nieuwe functionaliteit.” Een plaats op een populariteitsranglijst alleen kan die redenering niet leveren. Enquêtes kunnen wijzen op een grote gemeenschap of een ruime groep beschikbare ontwikkelaars; ze stellen niet vast wat de beste taal voor een bepaalde werklast is.

Belangrijkste punten

  • Controleer randvoorwaarden van het platform voordat je naar voorkeuren kijkt.
  • Bibliotheken en hulpmiddelen verbinden een taal met het probleemdomein.
  • Meet het knelpunt voordat je vooral op snelheid kiest.
  • Geheugenveiligheid en statische controles verminderen sommige fouten, maar de logica moet nog steeds worden getest.
  • Vaardigheden van het team, bestaande code en onderhoudskosten horen bij de beslissing.
  • Verschillende onderdelen kunnen verschillende talen rechtvaardigen.

Programmeertalen: voorbeelden

6 Walkthroughs

1

Volg een programma van broncode tot uitvoering

13 stappen
Volg vertaallagen, bekijk een build en vergelijk uitvoeringsroutes.
  1. 1
    IntroductieEén berekening doorkruist verschillende lagen

    Je schrijft één berekening, maar een processor heeft kleine gecodeerde instructies nodig die machinecode worden genoemd. Een vertaalprogramma levert de ontbrekende details.

    Volg x = a + b vanaf de broncode naar lagere niveaus en volg het programma daarna door het bouwen en uitvoeren. Houd twee vragen apart: in welke vorm is de code en welke software voert die uit? Een compiler, een interpreter en een JIT-compiler beantwoorden de tweede vraag verschillend. Dat onderscheid verklaart zowel overdraagbaarheid als het moment waarop fouten optreden.

  2. 2
    VoorbeeldEén berekening wordt meerdere handelingen

    Stel je voor x = a + b vier handelingen op een lager niveau voor:

    1. Laad de waarde van a in een register.
    2. Laad de waarde van b in een ander register.
    3. Tel de twee registerwaarden op.
    4. Sla het resultaat voor x op.

    De instructie in een hogere programmeertaal verbergt de registerkeuze en opslagdetails. De daadwerkelijk gegenereerde instructies kunnen verschillen omdat een compiler het werk kan optimaliseren. De vereiste berekening blijft gelijk.

  3. 3
    UitlegLeesbare instructies richten zich nog steeds op een processor

    Assembleertaal gebruikt instructienamen zoals mov en add. Een assembler codeert die als machine-instructies.

    Voor x86-64 zet mov eax, 1 de waarde 1 in EAX; add eax, ebx telt EBX daarbij op. Die registers en coderingen horen bij die instructiesetarchitectuur. Bytes vervangen door leesbare namen helpt mensen, maar verandert de instructies niet in een overdraagbare beschrijving op hoog niveau.

  4. 4
    UitdagingVerplaats het programma naar een andere processor

    Een team heeft overdraagbare C-broncode en een native uitvoerbaar bestand voor x86-64. Het heeft een native uitvoerbaar bestand voor ARM nodig. Welk bestand kan het team als uitgangspunt gebruiken en welk werk is nodig?

    ---reveal---

    Gebruik de broncode en compileer die voor het ARM-doel, met de vereiste platformondersteuning. Het uitvoerbare bestand voor x86-64 bevat instructies voor een andere ISA. Overdraagbaarheid van broncode maakt een nieuwe vertaling mogelijk; ze maakt die bestaande machine-instructies geen native ARM-code.

  5. 5
    VoorbeeldHerken tokens vóór de structuur

    Bekijk total = price * 3; in C.

    Lexicale analyse vindt zes tokens: total, =, price, *, 3 en ;. Syntactische analyse groepeert vervolgens price * 3 als de expressie waarvan het resultaat aan total wordt toegekend.

    Dit zijn verschillende taken. Tokenisatie herkent de onderdelen; syntactische analyse bepaalt hoe ze in elkaar passen. Semantische analyse kan dan vragen controleren zoals of de namen zijn gedeclareerd en de operandtypen compatibel zijn.

  6. 6
    VoorbeeldVerplaats werk met constanten naar de build

    De bronexpressie 60 * 60 * 24 gebruikt alleen constanten. Constantevaluatie tijdens compilatie kan die vóór de uitvoering berekenen: eerst 3600, dan 86400.

    Codegeneratie kan dat resultaat gebruiken in plaats van instructies die de vermenigvuldigingen herhalen. Dit is een optimalisatie omdat ze verandert hoe het werk wordt uitgevoerd en tegelijk het vereiste resultaat behoudt. Een waarde vervangen door slechts een handige benadering zou niet door deze regel worden gerechtvaardigd.

  7. 7
    UitlegVerbind vertaalde bestanden met bibliotheekroutines

    Compilatie kan afzonderlijke objectbestanden produceren. Een linker verbindt die bestanden en lost referenties naar bibliotheekbewerkingen op.

    Statisch linken neemt bibliotheekcode op in het gebouwde uitvoerbare bestand. Dynamisch linken vertrouwt op een compatibele gedeelde bibliotheek die later wordt geladen. Voor geen van beide aanpakken hoeft de machine van de gebruiker de compiler voor compilatie vooraf te hebben, maar een dynamisch gelinkt uitvoerbaar bestand heeft nog steeds zijn afhankelijkheden tijdens de uitvoering nodig.

  8. 8
    UitdagingEen geslaagde build geeft het verkeerde antwoord

    Een C-programma wordt met succes gebouwd. Het rapport moet een gebruikte hoeveelheid van een beginhoeveelheid aftrekken, maar de code telt ze op. Heeft de compiler nagelaten een taalregel te controleren?

    ---reveal---

    Nee. Optellen kan geldige syntaxis met compatibele typen zijn en toch de verkeerde taak uitdrukken. Dit is een logische fout. De compiler controleert taalregels en genereert code; een test met een bekend verwacht resultaat is nodig om het verschil tussen de berekening en het bedoelde doel bloot te leggen.

  9. 9
    UitlegEen uitvoeringsomgeving kan tusseninstructies uitvoeren

    CPython compileert een geldig Python-bestand naar bytecode en voert die bytecode daarna uit in zijn virtuele machine. De processor voert de machine-instructies van de uitvoeringsomgeving uit terwijl die de Python-bewerkingen uitvoert.

    Java gebruikt ook bytecode: javac produceert klassebestanden voor een compatibele JVM. Bytecode hoort bij een instructiemodel van een uitvoeringsomgeving en mag daarom niet worden verward met een universele processorinstructieset.

  10. 10
    VoorbeeldVergelijk twee momenten waarop de uitvoering kan stoppen

    In dit geldige Python-bestand wordt de eerste regel uitgevoerd:

    print("start")
    x = 10
    print(x + "5")
    

    Het drukt “start” af voordat de laatste regel een TypeError veroorzaakt. Verwijder nu het laatste sluithaakje: CPython kan het bestand niet compileren, zodat geen enkele instructie in dat bestand wordt uitgevoerd. Een syntaxisfout tijdens de voorbereiding en een typefout tijdens een uitgevoerde bewerking stoppen het programma in verschillende fasen.

  11. 11
    UitlegCompileer veelgebruikte code tijdens de uitvoering

    Een JIT-compiler produceert machinecode terwijl het programma draait. Een uitvoeringsomgeving kan eerst interpreteren, veelgebruikte methoden observeren en die methoden compileren.

    Dit verschuift de kosten: compilatie gebruikt tijdens de uitvoering tijd en geheugen, terwijl herhaalde uitvoering daarna sneller kan worden. Een langlopende service kan meer baat hebben bij die opwarmfase dan een klein eenmalig script. Het resultaat hangt af van de implementatie en de werklast.

  12. 12
    UitdagingMaak onderscheid tussen overdraagbare bytecode en native snelheid

    Een Java-klassebestand draait via een compatibele JVM op twee verschillende ISA’s. Tijdens elke uitvoering compileert de uitvoeringsomgeving een veelgebruikte methode. Waarom kan één klassebestand worden gedeeld terwijl de gegenereerde machinecode verschilt?

    ---reveal---

    Het klassebestand bevat JVM-bytecode die beide compatibele uitvoeringsomgevingen begrijpen. Elke JIT richt zich op de eigen processor. De overdraagbaarheid komt door het gedeelde tussenformaat en compatibele uitvoeringsomgevingen; de machine-instructies die tijdens de uitvoering worden geproduceerd, blijven platformspecifiek.

  13. 13
    SamenvattingHoud niveaus en uitvoeringsroutes gescheiden

    Machinecode richt zich op een ISA; assembleertaal geeft de instructies ervan leesbare namen; broncode in een hogere programmeertaal verbergt meer hardwaredetails.

    Een compiler vertaalt code, een interpreter voert die uit en een JIT compileert tijdens de uitvoering. Een uitvoeringsomgeving kan deze methoden combineren. Om een echt systeem uit te leggen, benoem je de bronvorm, tussenvorm en native vorm en geef je daarna aan welke fase de syntaxis controleert en welke fase de bewerking uitvoert die kan mislukken.

2

Volg de betekenis door een kort programma

10 stappen
Lees grammatica, evaluatievolgorde, toestandsveranderingen en functiegrenzen.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVorm en betekenis beantwoorden verschillende vragen
  2. 2
    VoorbeeldGeldige code kan de verkeerde berekening groeperen
  3. 3
    UitlegDe eerste voorwaarde kan de tweede beschermen
  4. 4
    UitdagingVoorspel of de deling wordt uitgevoerd
  5. 5
    UitlegEen toekenning leest vóór de bijwerking
  6. 6
    VoorbeeldVolg elke bijwerking van de accumulator
  7. 7
    VoorbeeldVolg een aanroep en de teruggegeven waarde
  8. 8
    UitdagingEen lokale bijwerking kan de aanroeper onveranderd laten
  9. 9
    UitdagingVolg grammatica en toestand samen
  10. 10
    SamenvattingLees het uitgevoerde pad
3

Volg typen en gedeelde objecten

14 stappen
Vergelijk het moment van controleren, volg aliassen en bepaal wanneer geheugen kan worden vrijgegeven.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVolg waarden, objecten en opslag
  2. 2
    UitlegGetypeerde waarden kunnen tussen namen bewegen
  3. 3
    KernpuntInferentie en conversie beantwoorden verschillende vragen
  4. 4
    UitdagingLees cijfers als de waarde die werkelijk wordt teruggegeven
  5. 5
    UitlegRepresentatie beïnvloedt numerieke resultaten
  6. 6
    UitlegEen aanroepframe kan eerder eindigen dan een object
  7. 7
    VoorbeeldTwee namen verwijzen naar één lijst
  8. 8
    UitdagingEen parameter kan veranderen en daarna ergens anders naar verwijzen
  9. 9
    UitlegBepaal precies hoe opslag verkeerd wordt gebruikt
  10. 10
    VoorbeeldVolg referenties totdat de laatste verdwenen is
  11. 11
    KernpuntEen cyclus kan geen actief toegangspunt hebben
  12. 12
    UitlegEigenaarschap controleert toegang vóór de uitvoering
  13. 13
    UitdagingZoek uit waarom opruiming een lijst niet kan vrijmaken
  14. 14
    SamenvattingVolg drie verschillende beslissingen
4

Bescherm regels en kies relaties tussen objecten

12 stappen
Volg opdrachten waarvan de volgorde van belang is en gebruik daarna inkapseling, methodeoverschrijving en compositie.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieStappen hebben betrouwbare grenzen nodig
  2. 2
    VoorbeeldDe volgorde van bewerkingen verandert de gedeelde toestand
  3. 3
    UitlegMaak de grens van een procedure nuttig
  4. 4
    UitdagingVoorspel een bijwerking in omgekeerde volgorde
  5. 5
    UitlegGeef elke rekening een eigen toestand
  6. 6
    VoorbeeldControleer voordat je een saldo verandert
  7. 7
    UitdagingZoek een omzeilde regel
  8. 8
    VoorbeeldSpecialiseer zonder het contract te verbreken
  9. 9
    UitlegLaat het object zijn oppervlaktemethode kiezen
  10. 10
    VerbandBevat het onderdeel dat het werk doet
  11. 11
    UitdagingKies twee relaties in een nieuw ontwerp
  12. 12
    SamenvattingStem grenzen af op verantwoordelijkheden
5

Transformeer een verzameling en vraag een resultaat op

13 stappen
Volg een functionele verwerkingsketen en een declaratieve query, met controles op effecten en uitvoering.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieBeschrijf een transformatie of een verzoek
  2. 2
    UitlegZuiverheid beschrijft meer dan het teruggegeven getal
  3. 3
    VoorbeeldSelecteer eerst de even waarden
  4. 4
    VoorbeeldTransformeer en combineer de geselecteerde waarden
  5. 5
    UitdagingPas de verwerkingsketen toe in een nieuw geval
  6. 6
    UitlegGeef gedrag terug dat zijn factor onthoudt
  7. 7
    VoorbeeldVerklein het probleem totdat een direct geval geldt
  8. 8
    VoorbeeldLees SQL als een verzoek over rijen
  9. 9
    KernpuntBehoud het resultaat terwijl het plan verandert
  10. 10
    VoorbeeldVerbind twee feiten via een regel
  11. 11
    UitdagingLees een nieuwe query inclusief de grenswaarde
  12. 12
    UitdagingCombineer een query met een zuivere transformatie
  13. 13
    SamenvattingHoud de relatie tussen invoer en resultaat zichtbaar
6

Vergelijk stijlen en onderbouw een taalkeuze

10 stappen
Houd één taak gelijk en kies daarna hulpmiddelen voor onderdelen met verschillende randvoorwaarden.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieHoud de taak gelijk
  2. 2
    VoorbeeldHoud de taak gelijk terwijl je de stijl verandert
  3. 3
    VoorbeeldPlaats dezelfde regel in objecten of een query
  4. 4
    UitdagingBepaal of een ontwerp maar één stijl heeft
  5. 5
    UitlegSluit eerst niet-ondersteunde doelplatforms uit
  6. 6
    VoorbeeldKies voor een volledig weersysteem
  7. 7
    TipMeet wat de tijd verbruikt
  8. 8
    UitdagingVerdedig een keuze bij concurrerende randvoorwaarden
  9. 9
    UitdagingVerbind taalkeuze met programmaorganisatie
  10. 10
    SamenvattingGeef een reden die bij de eis hoort

Programmeertalen: oefenvragen

50 Oefenvragen in 5 sets

Set 1: Pas regels voor typen, objecten en query’s toe10 Oefenvragen

Vraag 1 van 10

Een team kan dezelfde overdraagbare broncode voor twee verschillende ISA’s compileren. Wat is de juiste relatie tussen de broncode en de native uitvoer?
  1. De broncode is gedeeld; beide uitvoerbestanden bevatten dezelfde ISA-code.
  2. De broncode moet verschillen; elk uitvoerbestand richt zich op een eigen ISA.
  3. De broncode is gedeeld; elk uitvoerbestand richt zich op een eigen ISA.
  4. De broncode moet verschillen; beide uitvoerbestanden bevatten dezelfde ISA-code.
Toon antwoordVerberg antwoord
Antwoord: C
Overdraagbare broncode kan dezelfde berekening voor verschillende doelmachines beschrijven, maar elke native build bevat instructies voor de doel-ISA. Dezelfde broncode betekent niet dat de machinecode identiek is. Verschillende broncode vereisen zou overdraagbaarheid op bronniveau verwarren met de platformspecifieke uitvoer van de vertaling.

Vraag 2 van 10

Een uitvoeringsomgeving vertaalt een veel uitgevoerde methode naar native instructies nadat de applicatie is gestart. Welke techniek is dit?
  1. Just-in-timecompilatie
  2. Compilatie vooraf
  3. Statisch linken
  4. Lexicale analyse
Toon antwoordVerberg antwoord
Antwoord: A
Just-in-timecompilatie maakt machinecode tijdens de uitvoering, vaak met informatie over veelgebruikte code. Compilatie vooraf vindt vóór de uitvoering plaats en past daarom niet bij het genoemde moment. Linken verbindt programmaonderdelen; het verklaart niet deze vertaling van een methode tijdens de uitvoering.

Vraag 3 van 10

Welke waarden leveren a + b / 2 en (a + b) / 2 in Python respectievelijk op bij a = 8 en b = 12?
  1. 10,0 en 10,0
  2. 14,0 en 14,0
  3. 10,0 en 14,0
  4. 14,0 en 10,0
Toon antwoordVerberg antwoord
Antwoord: D
Deling heeft voorrang in de eerste expressie, dus de helft van 12 wordt bij 8 opgeteld, wat 14,0 geeft. De haakjes in de tweede expressie zorgen dat eerst de som 20 wordt berekend en daarna wordt gehalveerd tot 10,0. Beide als gemiddelden behandelen zou het verschil in groepering negeren dat hun betekenis verandert.

Vraag 4 van 10

Een C-functie geeft een toegewezen blok vrij, maar latere code leest het via een oude pointer. Welke fout ontstaat door die latere leesbewerking?
  1. Een geheugenlek
  2. Gebruik na vrijgave
  3. Een dubbele vrijgave
  4. Een bufferoverloop
Toon antwoordVerberg antwoord
Antwoord: B
De pointer leidt nog steeds naar opslag waarvan de toewijzing is geëindigd, dus die lezen is gebruik na vrijgave. Voor een dubbele vrijgave zou nog een vrijgavebewerking nodig zijn, en die wordt niet beschreven. Het probleem is de ongeldige levensduur van het doel, niet een genoemde index buiten de grenzen of het niet vrijgeven ervan.

Vraag 5 van 10

Een klasse definieert een attribuut balance en een methode deposit. Er worden twee rekeningen met verschillende beginsaldi aangemaakt. Waar horen die verschillende saldi thuis?
  1. In de gedeelde definitie van deposit
  2. In de twee rekeninginstanties
  3. In de naam van de klasse zelf
  4. In de parameternamen van de constructor
Toon antwoordVerberg antwoord
Antwoord: B
De twee objecten bevatten elk hun eigen instantiegegevens terwijl ze hetzelfde door de klasse gedefinieerde gedrag gebruiken. Een aanroep van deposit werkt op de ontvangende instantie. De methodedefinitie is gedeelde code, dus die zien als de plek waar beide huidige saldi staan zou gedrag verwarren met de toestand per object.

Vraag 6 van 10

Een fietsobject slaat een remsysteemobject op en vraagt dat de wielen te stoppen. Welke relatie past het best bij dit ontwerp?
  1. Compositie: de fiets heeft een remsysteem.
  2. Overerving: de fiets is een remsysteem.
  3. Methodeoverschrijving: de fiets vervangt het remsysteem.
  4. Ducktyping: de fiets is een subklasse van een remsysteem.
Toon antwoordVerberg antwoord
Antwoord: A
Het remsysteem is een onderdeel dat door de fiets wordt bevat en gebruikt, dus compositie drukt de heeft-eenrelatie uit. Van het remsysteem erven zou stellen dat een fiets een soort remsysteem is. Toegang tot de methoden van een onderdeel is niet genoeg om die is-eenrelatie te rechtvaardigen.

Vraag 7 van 10

Een verwerkingsketen behoudt uit [1, 4, 7, 10] de waarden groter dan 3, trekt van elke behouden waarde 1 af en telt ze daarna op. Wat is het resultaat?
  1. 21
  2. 17
  3. 18
  4. 22
Toon antwoordVerberg antwoord
Antwoord: C
Eerst filteren behoudt 4, 7 en 10. Van elk 1 aftrekken geeft 3, 6 en 9, samen 18. Een resultaat van 17 zou vier keer een waarde van de geselecteerde som van 21 aftrekken en daarmee het uitgesloten oorspronkelijke element als nog een getransformeerd element meetellen; alleen de drie behouden waarden worden getransformeerd.

Vraag 8 van 10

Een database kan een ongewijzigde SQL-query uitvoeren door rijen te doorlopen of een beschikbare index te gebruiken. Wat kiest tussen die plannen?
  1. De syntactische analyser voor SQL-query’s
  2. De linker van de database
  3. De typecontroleur voor SQL
  4. De queryoptimizer
Toon antwoordVerberg antwoord
Antwoord: D
De queryoptimizer selecteert een uitvoeringsplan dat het gevraagde resultaat oplevert. Een index kan een ander plan nuttig maken zonder de SQL te veranderen. De veldnamen in de query beschrijven de gevraagde gegevens, maar kiezen niet zelf de fysieke ophaalmethode van de engine.

Vraag 9 van 10

Een methode van een Python-klasse gebruikt een lus om een lijst bij te werken en een zuivere functie om elk element te transformeren. Hoe moeten de stijlen worden beschreven?
  1. Objectorganisatie die imperatief werk uitsluit
  2. Functionele organisatie die elke methode zuiver maakt
  3. Objectorganisatie met imperatief en functioneel werk
  4. Imperatieve organisatie die objectmethoden verhindert
Toon antwoordVerberg antwoord
Antwoord: C
De klasse geeft de methode een objectgeoriënteerde plek, de lus toont toestandsveranderingen en de zuivere hulpfunctie levert een functionele berekening. Deze beschrijvingen kunnen op verschillende niveaus tegelijk gelden. Eén hulpfunctie zuiver noemen maakt de omringende methode niet zuiver wanneer die methode ook een lijst verandert.

Vraag 10 van 10

Wat zijn x en y na deze Python-code?

x = [12]
y = x
x = x + [3]
y.append(6)
  1. x is [12, 3]; y is [12, 6].
  2. x is [12, 3, 6]; y is [12, 3, 6].
  3. x is [12, 3, 6]; y is [12].
  4. x is [12, 6]; y is [12, 3].
Toon antwoordVerberg antwoord
Antwoord: A
Aanvankelijk delen beide namen één lijst. Het optellen van lijsten maakt een nieuwe lijst [12, 3] en koppelt alleen x opnieuw; toevoegen via y verandert daarna de oorspronkelijke lijst in [12, 6]. Als je verwacht dat beide namen elk toegevoegd element tonen, behandel je het optellen van lijsten als ter plekke toevoegen en mis je de tussentijdse nieuwe koppeling.
Set 2: Lees code en herken de uitvoeringsroute10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een ontwikkelaar vervangt ruwe instructiebytes door namen zoals mov en jmp. Op welk taalniveau is het programma nu geschreven?
  1. Machinecode
  2. Assembleertaal
  3. Een hogere programmeertaal
  4. Een databasequerytaal
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Tijdens een build verbindt een hulpmiddel verschillende objectbestanden en lost het een aanroep naar een bibliotheekroutine op. Welk hulpmiddel voert deze taak uit?
  1. Een assembler
  2. Een syntactische analyser
  3. Een interpreter
  4. Een linker
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een Python-bestand bevat op de eerste regel een afdrukinstructie en op de laatste regel een niet-gesloten haakje. Wat gebeurt er wanneer CPython het bestand uitvoert?
  1. Het meldt een syntaxisfout voordat de afdrukinstructie wordt uitgevoerd.
  2. Het drukt de eerste regel af en meldt daarna een syntaxisfout.
  3. Het drukt de eerste regel af en veroorzaakt daarna een uitvoeringsfout.
  4. Het slaat alleen de laatste regel over en voert de geldige regels uit.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een geldig Python-programma drukt het verkeerde gemiddelde af omdat het één waarde optelt nadat het de andere heeft gehalveerd. Wat voor soort fout is dit?
  1. Een syntaxisfout
  2. Een uitvoeringsfout
  3. Een logische fout
  4. Een typeconflictfout
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Welke eindwaarde slaat deze Python-code op in total?

total = 1
for n in [4, 7, 2]:
    total = total + n
  1. 13
  2. 3
  3. 15
  4. 14
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

In Python wordt label = "18" gevolgd door label = 18. De naam kan van type veranderen, maar "18" + 2 veroorzaakt een fout. Hoe worden deze twee feiten beschreven?
  1. Statische typering met strikte regels voor optellen van gemengde typen
  2. Dynamische typering met strikte regels voor optellen van gemengde typen
  3. Dynamische typering met automatisch optellen als getallen
  4. Statische typering met automatisch optellen als getallen
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Wat bevat first nadat deze Python-code is uitgevoerd?

first = [4, 8]
second = first
second.append(6)
  1. [4, 8]
  2. [6]
  3. [4, 8, 6]
  4. [4, 8, [6]]
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een rapportageprogramma is opgedeeld in read_data(), calculate_total() en save_report(), die elk geordende opdrachten bevatten. Welke organisatie illustreert dit?
  1. Procedureel programmeren
  2. Logisch programmeren
  3. Zuiver functioneel programmeren
  4. Overerving van klassen
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een recursieve Python-functie geeft 0 terug bij n == 0; anders roept ze zichzelf aan met n + 1. Waarom zorgt het opgeschreven basisgeval er niet voor dat de berekening eindigt als die begint bij de positieve waarde 2?
  1. De invoer bereikt nul, maar het teruggeven van nul start de recursie opnieuw.
  2. Het basisgeval garandeert terminatie, ook bij toenemende invoer.
  3. De recursie wikkelt af richting nul voordat ze het basisgeval controleert.
  4. De recursieve invoer beweegt van nul af in plaats van nul te bereiken.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een dashboardverzoek voert twee stappen na elkaar uit: 180 ms wachten op netwerkgegevens en daarna 20 ms voor lokale opmaak. Een nieuwe taal zou de opmaak terugbrengen tot 5 ms zonder de netwerkwachttijd te veranderen. Wat is de beste beoordeling van alleen deze verandering?
  1. Ze bespaart 15 ms, terwijl het netwerk de grootste vertraging blijft.
  2. Ze bespaart 150 ms, door de viervoudige versnelling op het verzoek toe te passen.
  3. Ze bespaart 5 ms, omdat de nieuwe opmaaktijd de besparing is.
  4. Ze bespaart 20 ms, omdat compilatie de opmaakstap wegneemt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 3: Volg aanroepen, effecten en objectcontracten10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een compiler vervangt minutes = 60 * 12 door de vooraf berekende waarde 720. Welke optimalisatie wordt gebruikt en wat moet die behouden?
  1. Dynamisch linken, met behoud van bibliotheekbestandsnamen
  2. Lexicale analyse, met behoud van elk bronteken
  3. JIT-opwarming, met behoud van aantallen aanroepen tijdens de uitvoering
  4. Constantevaluatie tijdens compilatie, met behoud van het vereiste gedrag
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Wat drukt dit Python-programma af?

score = 11
def change():
    score = 4
    return score + 2
print(change())
print(score)
  1. 6, dan 4
  2. 6, dan 11
  3. 4, dan 11
  4. 13, dan 11
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Wat is items na de aanroep?

items = [3]
def revise(values):
    values.append(8)
    values = [1]
    values.append(5)
revise(items)
  1. [3, 8]
  2. [1, 5]
  3. [3, 8, 5]
  4. [3]
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Twee objecten verwijzen naar elkaar. Alle referenties vanuit actieve programmawortels naar die objecten worden verwijderd. Waarom kan referentietelling alleen ze missen, terwijl het volgen van bereikbaarheid ze kan opruimen?
  1. De interne aantallen bereiken nul, dus bereikbaarheid volgen is nodig om objecten met nul referenties vrij te geven.
  2. De interne aantallen blijven positief, dus beide objecten moeten nog bereikbaar zijn.
  3. De interne aantallen blijven positief, maar geen van beide objecten is bereikbaar.
  4. De interne aantallen zijn gelijk aan het aantal wortels, dus beide objecten hebben nul referenties.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Vanaf 120 past de ene bewerking 25% korting toe en telt een andere er 8 bij op. Welke eindwaarden ontstaan bij eerst korting en dan optellen, en bij eerst optellen en dan korting?
  1. 96 en 98
  2. 98 en 98
  3. 98 en 96
  4. 96 en 96
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een rekeningmethode controleert een opnamebedrag voordat ze het opgeslagen saldo verandert. Welk ontwerpidee houdt deze regel bij de bijwerking die ze controleert?
  1. Inkapseling
  2. Overerving
  3. Constantevaluatie tijdens compilatie
  4. Type-inferentie
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een gespecialiseerde rekening rekent 2 aan kosten voor elke geslaagde opname en mag nooit een negatief saldo hebben. Het saldo is 43. Wat moet er gebeuren met een verzoek om 42 op te nemen?
  1. Accepteer het verzoek en laat het saldo op 1 uitkomen.
  2. Accepteer het verzoek en laat het saldo op −1 uitkomen.
  3. Weiger het verzoek nadat het saldo tot 1 is verlaagd.
  4. Weiger het verzoek en houd het saldo op 43.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een functie geeft een vaste berekening op basis van haar argumenten terug en drukt ook een logbericht af. Is de functie zuiver en waarom?
  1. Ja; het resultaat is voorspelbaar.
  2. Nee; afdrukken is een neveneffect.
  3. Ja; een logbericht is geen opgeslagen gegeven.
  4. Nee; alle code heeft neveneffecten.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een webpagina registreert showDetails voor een klikgebeurtenis. Waarom kan diezelfde afhandelaar nog steeds objectmethoden en imperatieve toekenningen gebruiken?
  1. Gebeurtenissen maken de afhandelaar alleen objectgeoriënteerd als die attributen verandert.
  2. Gebeurtenissen bepalen wanneer de afhandelaar draait; de inhoud kan andere stijlen gebruiken.
  3. Gebeurtenissen vereisen zuivere callbackfuncties, dus toekenningen moeten worden verplaatst.
  4. Gebeurtenissen maken de afhandelaar declaratief, dus de engine kiest de bijwerkingen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een nieuwe analysefunctionaliteit moet een gevestigde bibliotheek gebruiken die alleen voor Python beschikbaar is, en het team onderhoudt al Python-services binnen het vereiste middelenbudget. Welke genoemde feiten ondersteunen de keuze voor Python?
  1. Ruwe lussnelheid en afwezigheid van geheugenkosten tijdens de uitvoering
  2. Browsercompatibiliteit en afgedwongen typeaanduidingen in Python
  3. Exacte berekeningen met drijvendekommagetallen en elk hardwaredoel
  4. Ondersteuning van de vereiste bibliotheek en bestaande teamervaring
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 4: Redeneer over grenzen van uitvoering en ontwerp heen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een instructie in een hogere programmeertaal wordt in twee geoptimaliseerde builds een verschillend aantal instructies, maar beide voldoen aan hetzelfde door de taal vereiste gedrag. Maakt het verschil in instructieaantal een van beide builds incorrect?
  1. Nee; één instructie hoeft niet op een vast aantal machine-instructies uit te komen.
  2. Ja; elke instructie moet op één machine-instructie uitkomen.
  3. Ja; overdraagbaarheid op hoog niveau vereist identieke native uitvoer.
  4. Nee; geoptimaliseerde uitvoer mag vereiste resultaten veranderen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een native uitvoerbaar bestand gebruikt dynamisch linken. De ontvanger heeft geen compiler en mist een vereiste compatibele gedeelde bibliotheek. Welke genoemde afwezigheid verklaart waarom het mogelijk niet start?
  1. De compiler voor compilatie vooraf ontbreekt.
  2. Het oorspronkelijke bronbestand ontbreekt.
  3. De vereiste gedeelde bibliotheek ontbreekt.
  4. De syntaxisboom van de compiler ontbreekt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een compatibele JVM draait hetzelfde klassebestand op twee ISA’s en compileert op beide de meest gebruikte methode met JIT. Een teamgenoot beweert dat dit bewijst dat het klassebestand al native instructies voor beide processors bevat. Wat is de juiste diagnose?
  1. Het bestand bevat native code; de JVM linkt alleen platformbibliotheken.
  2. Het bestand bevat bytecode; de JIT-uitvoer is ook ISA-onafhankelijk.
  3. Het bestand bevat brontekst; de JVM moet de oorspronkelijke broncode opnieuw compileren.
  4. Het bestand bevat bytecode; elke JIT genereert eigen native code.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Vergelijk in Python bij n = 0 de expressie n != 0 and 36 / n > 4 met 36 / n > 4 and n != 0. Wat gebeurt er?
  1. Beide zijn onwaar omdat ze dezelfde twee tests bevatten.
  2. De eerste is onwaar; de tweede veroorzaakt een delingsfout.
  3. Beide veroorzaken een delingsfout omdat beide een deling noemen.
  4. De eerste veroorzaakt een fout; de tweede is onwaar.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Welke eindwaarde wordt opgeslagen in total?

def bump(x):
    return x + 2
total = 0
for n in [2, 5, 8]:
    if n > 3:
        total = total + bump(n)
  1. 17
  2. 15
  3. 19
  4. 21
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een compiler leidt vóór de uitvoering alle lokale variabeletypen af, hoewel de programmeur geen typeannotaties schrijft. Welke controleaanpak laat dit zien?
  1. Dynamische typering met inferentie tijdens de uitvoering
  2. Zwakke typering zonder type-informatie
  3. Dynamische typering met expliciete conversie
  4. Statische typering met type-inferentie
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een statisch getypeerde taal leidt uit een beginwaarde een type voor gehele getallen af en staat een bepaalde impliciete numerieke conversie toe. Wat kun je over deze twee eigenschappen concluderen?
  1. Inferentie maakt deze variabelen dynamisch ondanks de statische controle.
  2. Impliciete conversie betekent dat de afgeleide variabele geen vast type heeft.
  3. Inferentie en conversie kunnen samengaan met statische controle.
  4. Statische controle betekent dat de conversie expliciet moet worden geschreven.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een rapport doorloopt objecten en roept item.render() aan. Een nieuwe, niet-verwante klasse levert een compatibele methode render(). De aanroeper in Python vereist geen superklasse. Waarom kan het object in de lus worden opgenomen zonder een typespecifieke tak?
  1. Gemeenschappelijke overerving is vereist voordat de methode kan draaien.
  2. Ducktyping accepteert het object; dynamische methodekeuze selecteert de methode.
  3. Gelijke methodenamen laten elk ontvangend object dezelfde inhoud uitvoeren.
  4. De aanroeper moet vóór de aanroep de concrete klasse selecteren.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een fabrieksfunctie geeft een functie terug die haar invoer vermenigvuldigt met de factor van de fabrieksfunctie. De factor was 4 en de teruggegeven functie wordt later aangeroepen met 7. Welk resultaat en mechanisme verklaren dit?
  1. 28, met een stapelframe
  2. 11, met een closure
  3. 28, met een closure
  4. 28, met een typeconversie
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een team beschouwt een populariteitsranglijst als voldoende bewijs dat een taal op het vereiste doelplatform kan draaien. Welke compatibiliteitscontrole ontbreekt?
  1. Of het doelplatform de benodigde uitvoeringsomgeving ondersteunt
  2. Of handleidingen de inspringing gebruiken die het team verkiest
  3. Of de naam hoog staat in een andere populariteitsenquête
  4. Of de uitgavegeschiedenis korter is dan die van alternatieven
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 5: Onderzoek beperkingen en combineer de ideeën10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een implementatie op basis van een interpreter besteedt tijd aan het uitvoeren van bytecode. Een tweede implementatie van dezelfde taal voegt een JIT toe en wordt sneller bij een langdurige herhaalde berekening. Welke conclusie volgt zonder de definitie van de taal te veranderen?
  1. De taal werd statisch getypeerd doordat ze een compiler kreeg.
  2. De uitvoeringsstrategie veranderde terwijl de semantiek van de taal gelijk kan blijven.
  3. De eerste uitvoeringsomgeving voerde broncode direct uit, dus bytecode werd niet gecompileerd.
  4. De gemeenschappelijke taal moet de betekenis van operatoren veranderen om sneller te worden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Wat drukt dit Python-programma af?

value = 6
def step(value):
    value = value + 3
    return value
for n in [1, 2]:
    result = step(value)
print(value, result)
  1. 12 12
  2. 6 12
  3. 6 9
  4. 9 9
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een service met automatische opruiming van onbereikbare objecten slaat elk verwerkt record op in een globale lijst die bereikbaar blijft. Lokale variabelen verwijderen vermindert het geheugen voor bewaarde records niet. Welke verandering pakt de genoemde oorzaak aan?
  1. Verwijder referenties naar verouderde records uit de globale lijst.
  2. Roep de opruiming herhaaldelijk aan en laat het archief ongewijzigd.
  3. Verwijder elke lokale koppeling en behoud de globale archiefregels.
  4. Vervang de archieflijst door een kopie met dezelfde records.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Twee procedures werken dezelfde globale waarde bij. double() vermenigvuldigt die met 2; add_three() telt er 3 bij op. Wat is bij dezelfde beginwaarde altijd het verschil tussen eerst verdubbelen en dan optellen en eerst optellen en dan verdubbelen?
  1. Eerst verdubbelen en dan optellen geeft een resultaat dat 3 hoger is.
  2. Eerst optellen en dan verdubbelen geeft een resultaat dat 6 hoger is.
  3. Beide volgordes geven altijd hetzelfde resultaat.
  4. Eerst optellen en dan verdubbelen geeft een resultaat dat 3 hoger is.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een Java-klasse markeert haar saldoveld als private en biedt een opnamemethode met controle aan. Code in een niet-verwante externe klasse probeert rechtstreeks een negatief saldo toe te kennen. Wat beschermt de klasseregel bij die toegang?
  1. De opnamemethode controleert directe toekenningen.
  2. De toekenning maakt een afzonderlijk lokaal saldo aan.
  3. Private toegang staat schrijven toe maar blokkeert alle leesacties.
  4. De compiler wijst toegang tot het private veld af.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een klasse erft van een wachtrij alleen om opslag te hergebruiken, maar schakelt de verwijderbewerking van de wachtrij uit. Aanroepers die een bruikbare wachtrij verwachten lopen vast. Welk herontwerp pakt het genoemde relatieprobleem aan?
  1. Behoud overerving van de wachtrij en verberg verwijderen in de interface van de subklasse.
  2. Bevat intern een wachtrij en bied de bewerkingen aan die de klasse belooft.
  3. Behoud overerving van de wachtrij en laat verwijderen ongemerkt niets doen.
  4. Behoud overerving van de wachtrij en leg vast dat aanroepers verwijderen moeten vermijden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een tabel readings heeft rijen (level, active): (50, true), (65, true), (20, false), (85, true) en (90, false). Een query vraagt SUM(level) op waar active = true AND level > 50. Welk totaal is vereist nadat een geschikte index is toegevoegd en mag het uitvoeringsplan veranderen?
  1. 150; het plan mag veranderen met behoud van het totaal.
  2. 200; het plan mag veranderen met behoud van het totaal.
  3. 240; het plan mag veranderen met behoud van het totaal.
  4. 150; de nieuwe index mag zowel het plan als het totaal veranderen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Prolog heeft parent(lea, noah). en parent(noah, sora). plus de regel grandparent(X, Z) :- parent(X, Y), parent(Y, Z).. Wat vindt grandparent(lea, Who)?
  1. Who = noah
  2. Who = lea
  3. Who = sora
  4. Zowel Who = lea als Who = noah
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een imperatieve lus, een objectmethode en een SQL-query moeten bedragen strikt boven 50 optellen in [50, 65, 20, 85]. Wat moeten hun correcte resultaten zijn?
  1. 200 in alle drie de versies
  2. 150 in alle drie de versies
  3. 150 voor de lus, 200 voor de methode en query
  4. 200 voor de lus, 150 voor de methode en query
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een functie geeft een nieuw toegewezen lijst terug en de aanroeper slaat die lijst op voor later gebruik. Moet de lijst verdwijnen wanneer het aanroepframe van de functie wordt verwijderd?
  1. Ja; het einde van de aanroep heft de referentie van de aanroeper op.
  2. Ja; teruggeven kopieert de inhoud en geeft de lijst vrij.
  3. Nee; het frame dat de lijst maakte moet actief blijven om die te behouden.
  4. Nee; de aanroeper heeft nog steeds een referentie naar de lijst.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

94 Kernbegrippen, elk met een definitie

A

aanroepstapel
De aanroepstapel houdt actieve functieaanroepen in geneste volgorde bij, zodat de uitvoering naar de juiste aanroeper kan terugkeren.
Een aanroep van een functie area voegt een item toe aan de aanroepstapel en verwijdert dat wanneer het resultaat naar de aanroeper terugkeert.
abstractie
Abstractie verbergt implementatiedetails achter een eenvoudiger model of interface, zodat een programmeur zich kan richten op de taak die wordt beschreven.
Een variabele is een abstractie waarmee code een waarde kan benoemen zonder het fysieke geheugenadres uit te schrijven.
aliasing
Aliasing ontstaat wanneer verschillende namen of referenties naar hetzelfde object leiden, waardoor mutaties ervan via al die namen of referenties zichtbaar zijn.
Na b = a verklaart aliasing waarom toevoegen via b ook kan veranderen wat via a zichtbaar is.
assembleertaal
Assembleertaal geeft instructies op laag niveau weer met leesbare mnemonics en namen voor registers of opslaglocaties.
Een analist leest assembleertaal om bewerkingen te volgen in een programma waarvan de broncode op hoog niveau niet beschikbaar is.
assembler
Een assembler is een vertaalprogramma dat instructies in assembleertaal codeert als machinecode voor een specifieke instructieset.
Een assembler zet add eax, ebx om in de bijbehorende x86-64-instructiebytes.
attribuut
Een attribuut is een benoemd gegeven of lid dat bij een object of klasse hoort en via dat object of die klasse wordt benaderd.
Het saldoattribuut van een rekening legt de huidige toestand vast, afzonderlijk van het saldo van een andere rekening.
automatische opruiming van onbereikbare objecten
Automatische opruiming van onbereikbare objecten identificeert en hergebruikt automatisch opslag van objecten die het draaiende programma niet meer kan bereiken.
Automatische opruiming die bereikbaarheid volgt, kan een onbereikbare groep opruimen, ook wanneer de objecten nog naar elkaar verwijzen.

B

basisgeval
Een basisgeval geeft een recursief resultaat rechtstreeks, zodat een eindige berekening kan stoppen met verdere recursieve aanroepen.
Het basisgeval van de faculteit geeft 1 terug voor nul, zodat de wachtende vermenigvuldigingsaanroepen kunnen worden afgerond.
bereik
Het bereik is het deel van een programma waar een bepaalde naam beschikbaar is, onafhankelijk van de levensduur van het object waarnaar die verwijst.
Het bereik van een lokale naam strekt zich niet uit tot een aanroeper alleen omdat de functie het object teruggeeft dat die naam aanduidde.
bufferoverloop
Een bufferoverloop schrijft voorbij de grenzen van een opslagblok en kan aangrenzende gegevens of de programmatoestand beschadigen.
Naar index 10 van een C-array met tien elementen schrijven veroorzaakt een bufferoverloop in plaats van een elfde element toe te voegen.
bytecode
Bytecode is een tussenvorm van instructies die is ontworpen voor een uitvoeringsomgeving in software in plaats van voor rechtstreekse uitvoering door de processor waarop die draait.
CPython genereert bytecode uit een geldig Python-bestand voordat de virtuele machine het programma uitvoert.

C

callbackfunctie
Een callbackfunctie is een functie die aan andere code wordt doorgegeven zodat die code haar kan aanroepen wanneer een bewerking of gebeurtenis dat vereist.
Door showMenu als callbackfunctie door te geven, kan een knopsysteem die functie aanroepen wanneer de gebruiker klikt.
closure
Een closure is een functie samen met toegang tot variabelen uit de omgeving waarin ze is gemaakt; die toegang kan langer bestaan dan de omsluitende aanroep.
Een closure die vermenigvuldigt behoudt haar factor nadat de functie die haar maakte is teruggekeerd.
compilatie vooraf
Compilatie vooraf vertaalt code vóór de uitvoering van een programma en verplaatst het vertaalwerk naar een aparte bouwfase.
Compilatie vooraf kan een native uitvoerbaar bestand produceren dat draait zonder dat de compiler is geïnstalleerd.
compiler
Een compiler vertaalt broncode naar een doelrepresentatie, die native machine-instructies of bytecode als tussenvorm kan zijn.
Een compiler kan een incompatibele toekenning afwijzen voordat het programma wordt geproduceerd dat een gebruiker gaat uitvoeren.
compositie
Compositie bouwt een object op uit andere objecten die het als onderdelen of samenwerkingspartners bevat en drukt een heeft-eenrelatie uit.
Compositie geeft een auto een motorobject waarvan de auto de methoden kan aanroepen zonder zelf een soort motor te worden.
constantevaluatie tijdens compilatie
Constantevaluatie tijdens compilatie berekent expressies die uit constanten bestaan tijdens de compilatie en behoudt daarbij het vereiste programmagedrag.
Constantevaluatie tijdens compilatie laat een compiler 60 * 60 * 24 vervangen door 86400 voordat het programma start.

D

declaratief programmeren
Declaratief programmeren specificeert een vereist resultaat of een vereiste relatie en laat uitvoeringsdetails over aan een taalengine.
Declaratief programmeren laat een SQL-query rijen opvragen die voldoen, zonder een lus rij voor rij op te geven.
domeinspecifieke taal
Een domeinspecifieke taal is ontworpen voor een bepaald probleemgebied, zoals databasequery’s, patronen of paginaopmaak.
Een domeinspecifieke taal zoals SQL kan een gegevensverzoek directer uitdrukken dan een met de hand geschreven zoeklus.
drijvendekommagetal
Een drijvendekommagetal geeft een waarde weer met beperkte binaire precisie, waardoor de meeste decimale breuken worden benaderd.
Een drijvendekommagetal kan een klein verschil veroorzaken tussen 0.1 + 0.2 en de opgeslagen waarde van 0.3.
dubbele vrijgave
Een dubbele vrijgave probeert hetzelfde toegewezen geheugenblok tweemaal vrij te geven en doorbreekt daarmee de verwachte levenscyclus van de geheugentoewijzer.
free opnieuw aanroepen op een al vrijgegeven toewijzing kan een dubbele vrijgave en onvoorspelbaar gedrag veroorzaken.
ducktyping
Ducktyping accepteert een object op basis van de bewerkingen die het biedt, zonder noodzakelijk een gedeclareerde gemeenschappelijke superklasse te vereisen.
Ducktyping in Python laat een niet-verwant object deelnemen aan een oppervlakteberekening als het een compatibele methode area() biedt.
dynamische methodekeuze
Dynamische methodekeuze selecteert tijdens de uitvoering een methode-implementatie op basis van het object dat de aanroep ontvangt.
Dynamische methodekeuze leidt shape.area() naar de implementatie voor rechthoeken wanneer het ontvangende object een rechthoek is.
dynamische typering
Dynamische typering controleert typen van waarden tijdens de uitvoering en staat toe dat een naam in de loop van de tijd naar waarden van verschillende typen verwijst.
Dynamische typering in Python laat een naam eerst naar een geheel getal en later naar een tekenreeks verwijzen, maar incompatibele rekenkundige bewerkingen mislukken nog steeds.

E

ecosysteem
Een ecosysteem is de omringende verzameling bibliotheken, hulpmiddelen, documentatie en gemeenschapsondersteuning die voor een technologie beschikbaar is.
Een sterk ecosysteem voor data-analyse kan Python praktisch maken voor een team, ook wanneer ruwe lussnelheid niet het belangrijkste voordeel is.
eersteklasfunctie
Een eersteklasfunctie kan als waarde worden behandeld, waaronder toekennen aan een naam, doorgeven als argument en teruggeven uit een aanroep.
Een eersteklasfunctie kan aan map worden doorgegeven als de bewerking die op elk element van een verzameling wordt toegepast.
eigenaarschap
Eigenaarschap kent verantwoordelijkheid voor de levensduur van een waarde toe, zodat compilerregels toegang kunnen controleren en geheugenvrijgave kunnen regelen.
Eigenaarschap in Rust wijst toegang in veilige code af via een referentie die langer zou bestaan dan de waarde waarnaar die verwijst.
expressie
Een expressie is een stuk code dat wordt geëvalueerd om een waarde op te leveren die vervolgens door een omringende instructie kan worden gebruikt.
De expressie width * height levert de waarde die een functie voor de oppervlakte van een rechthoek teruggeeft.

F

functie van hogere orde
Een functie van hogere orde ontvangt een functie als argument of geeft een functie terug, zodat gedrag zelf kan worden gecombineerd.
De functie van hogere orde filter ontvangt een predicaat dat bepaalt welke invoerwaarden behouden blijven.
functioneel programmeren
Functioneel programmeren organiseert berekeningen door functies te combineren en nieuwe waarden te produceren, terwijl gedeelde mutatie wordt beperkt.
Functioneel programmeren kan een rapportberekening uitdrukken als selectie, transformatie en combinatie van waarden.

G

gebeurtenisafhandelaar
Een gebeurtenisafhandelaar is een functie of bewerking die is geregistreerd om te reageren wanneer de uitvoeringsomgeving een bepaalde gebeurtenis levert.
Een gebeurtenisafhandelaar voor klikken kan een menu bijwerken terwijl een andere afhandelaar een binnenkomend netwerkbericht verwerkt.
gebeurtenisgestuurd programmeren
Gebeurtenisgestuurd programmeren organiseert werk rond afhandelaars die worden aangeroepen wanneer gebeurtenissen plaatsvinden, zoals klikken of binnenkomende berichten.
Gebeurtenisgestuurd programmeren wacht op een klik op een knop voordat de geregistreerde interfacebijwerking wordt uitgevoerd.
gebruik na vrijgave
Bij gebruik na vrijgave wordt via een pointer toegang verkregen tot opslag nadat die is vrijgegeven, wat ongeldige geheugentoegang veroorzaakt.
Gebruik na vrijgave kan een nieuw object overschrijven dat toevallig een eerder vrijgegeven blok inneemt.
gegevenstype
Een gegevenstype bepaalt een verzameling mogelijke waarden en de bewerkingen die daarop zijn toegestaan, en stuurt zo hun representatie en gebruik.
Een gegevenstype voor tekenreeksen ondersteunt tekstbewerkingen, ook wanneer de tekens in de waarde toevallig cijfers zijn.
geheugenlek
Een geheugenlek houdt toegewezen opslag vast die niet meer nodig is, waardoor minder geheugen beschikbaar is voor nuttig werk.
Een steeds groeiend cachegeheugen waaruit niets wordt verwijderd, kan ook in een applicatie met automatische opruiming een geheugenlek veroorzaken.
geheugentoewijzing
Geheugentoewijzing reserveert geheugen voor programmagegevens; de gereserveerde opslag blijft niet beschikbaar voor hergebruik totdat die wordt vrijgegeven.
Een groeiende verzameling maken kan verdere geheugentoewijzing vereisen wanneer die extra elementen opslaat.
geheugenveiligheid
Geheugenveiligheid voorkomt categorieën ongeldige geheugentoegang, zoals toegang tot vrijgegeven opslag of schrijven buiten geldige grenzen.
Geheugenveiligheid kan een gevaarlijke toegang blokkeren terwijl het programma nog steeds een niet-verwante fout in de berekening bevat.
gestructureerd programmeren
Gestructureerd programmeren organiseert de besturing met sequenties, selecties, lussen en aanroepen in plaats van willekeurige sprongen.
Gestructureerd programmeren laat een lezer geneste blokken volgen om te zien welke instructie daarna wordt uitgevoerd.

H

heap
De heap is geheugen voor dynamisch toegewezen objecten, waaronder objecten die langer kunnen bestaan dan de functie die ze maakt.
Een teruggegeven lijst kan op de heap blijven staan nadat de functie die de lijst maakte is voltooid en het aanroepframe is verdwenen.
hogere programmeertaal
Een hogere programmeertaal drukt programma’s uit met begrippen zoals variabelen en functies en verbergt daarbij veel hardwaredetails.
Een hogere programmeertaal laat een ontwikkelaar een optelling opgeven zonder te kiezen welke processorregisters worden gebruikt.

I

imperatief programmeren
Imperatief programmeren drukt een berekening uit als geordende opdrachten die de programmatoestand bijwerken via toekenningen en besturingsverloop.
Imperatief programmeren maakt de nieuwe waarde van een accumulator bij elke doorgang door een lus expliciet.
impliciete typeconversie
Impliciete typeconversie is een conversie die taalregels automatisch uitvoeren wanneer een bewerking een waarde van een ander type nodig heeft.
Impliciete typeconversie in JavaScript zet numerieke tekst om voor vermenigvuldiging, maar kan die bij optelling samenvoegen.
informatieverberging
Informatieverberging houdt implementatiedetails achter een interface, zodat andere code in plaats daarvan afhangt van de beloofde bewerkingen.
Informatieverberging laat een rekening haar opslagrepresentatie veranderen met behoud van het stortingsgedrag.
inkapseling
Inkapseling groepeert gegevens met gecontroleerde bewerkingen die de regels ervoor bewaken en onveilige veranderingen door andere code beperken.
Inkapseling laat een rekening een te hoge opname weigeren voordat het saldo negatief kan worden.
instructie
Een instructie is een volledige programmaopdracht, zoals een toekenning, een voorwaardelijk blok of een lus.
De instructie total = price * 3 kent het resultaat van een vermenigvuldiging toe aan een variabele.
instructiesetarchitectuur
Een instructiesetarchitectuur bepaalt welke bewerkingen een processorfamilie ondersteunt en welke coderingen worden gebruikt om die weer te geven.
Een uitvoerbaar bestand voor x86-64 richt zich op een andere instructiesetarchitectuur dan native ARM-machinecode.
interface
Een interface specificeert bewerkingen die implementerende typen moeten bieden, zodat aanroepers op een gemeenschappelijk contract kunnen vertrouwen.
Een interface voor vormen kan een methode area() vereisen en elke vorm die anders laten berekenen.
interpreter
Een interpreter voert een programma uit via een draaiend softwaresysteem dat broncode of een interne representatie kan uitvoeren.
Een interpreter kan een kort script uitvoeren zonder de gebruiker eerst een apart native uitvoerbaar bestand te geven.

J

just-in-timecompilatie
Just-in-timecompilatie genereert tijdens de uitvoering van een programma native machinecode, vaak voor veelgebruikte delen.
Just-in-timecompilatie kan een langlopende lus versnellen nadat de uitvoeringsomgeving herhaalde uitvoering heeft waargenomen.

K

klasse
Een klasse definieert de structuur en het gedrag van haar instanties en beschrijft hun attributen en beschikbare methoden.
Een klasse Loan van een bibliotheek kan voor elke uitlening het lid, het boek, de inleverdatum en de verlengbewerking definiëren.
knelpunt
Een knelpunt is het onderdeel van een systeem dat de algehele prestaties beperkt, waardoor verbeteringen elders minder effectief zijn.
Als wachten op het netwerk het knelpunt is, kan snellere lokale rekenkunde de totale tijd van het verzoek nauwelijks veranderen.
kortsluitevaluatie
Kortsluitevaluatie slaat een logische operand over wanneer een eerdere operand de uitkomst van de expressie al bepaalt.
Kortsluitevaluatie laat een controle op ongelijkheid aan nul een onveilige deling aan de rechterkant van and voorkomen.

L

lambda-expressie
Een lambda-expressie is een functie-expressie zonder gedeclareerde functienaam, nuttig om een kleine bewerking als waarde door te geven.
De Python-lambda-expressie lambda n: n * n levert een kwadrateerbewerking aan een transformatieketen.
lexicale analyse
Lexicale analyse splitst brontekst op in betekenisvolle tokens, waaronder identifiers, letterlijke waarden, operatoren en leestekens.
Tijdens lexicale analyse wordt price * 3 gescheiden in een naam, een vermenigvuldigingsoperator en een getal.
linker
Een linker verbindt vertaalde objectbestanden en vereiste bibliotheekcode en lost referenties tussen de onderdelen van een programma op.
Een linker verbindt een vertaalde aanroep uit een bronbestand met de afdrukroutine die een bibliotheek levert.
logisch programmeren
Logisch programmeren legt feiten en regels vast en zoekt daarna naar waarden die bij een vraag aan die relaties voldoen.
Logisch programmeren kan een grootouderrelatie afleiden door twee bekende ouderrelaties te verbinden.
logische fout
Een logische fout zorgt ervoor dat een programma een onbedoeld resultaat berekent, ook al kunnen de uitgevoerde bewerkingen met succes worden voltooid.
a + b / 2 gebruiken om het gemiddelde van twee waarden te berekenen is een logische fout omdat de deling vóór de optelling plaatsvindt.

M

machinecode
Machinecode is een reeks binair gecodeerde processorinstructies die een bepaalde instructiesetarchitectuur volgen.
De processor voert machinecode uit nadat een compiler of assembler compatibele instructies heeft geproduceerd.
methode
Een methode is een bewerking die bij een klasse of object hoort en vaak werkt op de toestand van de instantie die de aanroep ontvangt.
Een opnamemethode controleert het gevraagde bedrag voordat ze het saldo van de ontvangende rekening verandert.
methodeoverschrijving
Methodeoverschrijving geeft een subklasse een eigen implementatie van een geërfde methode en houdt de bewerking beschikbaar voor aanroepers.
Een opnamemethode overschrijven kan kosten toevoegen met behoud van de regel tegen een negatief saldo.
multiparadigmataal
Een multiparadigmataal ondersteunt meerdere stijlen om berekeningen te organiseren, zodat verschillende aanpakken binnen één programma mogelijk zijn.
Python is een multiparadigmataal waarin een klassenmethode een imperatieve lus en een functionele transformatie kan gebruiken.
mutatie
Een mutatie verandert een bestaand object ter plekke, zodat elke referentie naar datzelfde object de bijgewerkte inhoud waarneemt.
Een element toevoegen aan een gedeelde lijst is een mutatie; een naam aan een nieuwe lijst toekennen verandert alleen die koppeling.

N

neveneffect
Een neveneffect is een waarneembare handeling naast het teruggeven van een resultaat, waaronder uitvoer of veranderingen in gedeelde programmagegevens.
Een rapportbestand schrijven is een neveneffect, ook als de functie elke globale numerieke variabele onveranderd laat.

O

object
Een object is een programma-entiteit die gegevens combineert met beschikbare bewerkingen, vaak als specifieke instantie van een klasse.
Twee rekeningobjecten kunnen dezelfde opnamemethode gebruiken en tegelijk hun eigen verschillende saldi behouden.
onveranderlijkheid
Onveranderlijkheid betekent dat een bestaande waarde niet ter plekke kan worden veranderd; een bijwerking moet een andere waarde produceren of selecteren.
Onveranderlijkheid houdt een oorspronkelijke verzameling beschikbaar wanneer een transformatie een nieuwe versie maakt.
overerving
Overerving definieert een gespecialiseerde klasse op basis van een bestaande klasse en hergebruikt of breidt gedrag uit via een is-eenrelatie.
Overerving past bij een spaarrekening die nog steeds moet voldoen aan het gedrag dat van een bankrekening wordt verwacht.
overloop
Overloop ontstaat wanneer een rekenkundig resultaat buiten het bereik van een numerieke representatie met vaste bitbreedte ligt.
Overloop van gehele getallen in Java springt voorbij de maximumwaarde terug, terwijl overloop van gehele getallen met teken in C ongedefinieerd gedrag is.

P

parameter
Een parameter is een naam in een functiedefinitie die een door een argument geleverde waarde ontvangt wanneer de functie wordt aangeroepen.
De parameter width ontvangt 3 wanneer de aanroeper area(3, 4) evalueert.
platform
Een platform is de hardware- en softwareomgeving waarop een programma moet draaien, inclusief ondersteunde uitvoeringsomgevingen en systeemdiensten.
Een browserplatform kan JavaScript rechtstreeks via zijn engine uitvoeren en gecompileerde modules via WebAssembly.
polymorfisme
Polymorfisme laat een gedeelde bewerking met verschillende objecttypen werken, waarbij elk object het passende gedrag levert.
Polymorfisme laat een rapport area() op cirkels en rechthoeken aanroepen zonder voor elk een aparte tak.
procedureel programmeren
Procedureel programmeren groepeert imperatieve opdrachten in benoemde functies of procedures die elk een samenhangende taak uitvoeren.
Procedureel programmeren scheidt gegevens lezen, een resultaat berekenen en een rapport tonen in benoemde taken.
programmeerparadigma
Een programmeerparadigma is een stijl om berekeningen te organiseren, met specifieke manieren om gegevens, bewerkingen en besturing uit te drukken.
Het ene programmeerparadigma kan de nadruk leggen op toestandsveranderingen, terwijl een ander de nadruk legt op het combineren van zuivere functies.

Q

queryoptimizer
Een queryoptimizer kiest een plan om een databasequery uit te voeren en behoudt daarbij het resultaat dat de query vraagt.
Een queryoptimizer kan na een verandering in de gegevens een beschikbare index gebruiken zonder het SQL-verzoek te herschrijven.

R

recursie
Recursie lost een probleem op via aanroepen van dezelfde functie, waarbij het gewoonlijk wordt verkleind tot een geval dat geen verdere aanroep nodig heeft.
Faculteitsrecursie vermenigvuldigt met steeds kleinere gehele getallen tot het gedefinieerde resultaat voor nul is bereikt.
referentie
Een referentie is een waarde die een object identificeert en toegang ertoe geeft zonder de opgeslagen gegevens van het object te dupliceren.
Een referentie naar een lijst kopiëren geeft een andere naam toegang tot dezelfde lijst en latere mutaties ervan.
referentietelling
Referentietelling houdt referenties naar een object bij en maakt de opslag ervan normaal gesproken vrij wanneer het aantal nul bereikt.
Referentietelling in CPython heeft hulp van het opruimen van cycli nodig voor onbereikbare objecten die naar elkaar blijven verwijzen.
reguliere expressie
Een reguliere expressie beschrijft een tekstpatroon voor een zoekengine met regels voor tekens, herhaling en groepering.
De reguliere expressie [0-9]{3}-[0-9]{4} komt overeen met een groep van drie cijfers gevolgd door een koppelteken en vier cijfers.
REPL
Een REPL is een interactieve lees-evalueer-printlus die invoer accepteert, evalueert, een resultaat toont en op volgende invoer wacht.
Een cursist gebruikt een REPL om een kleine expressie te testen en het resultaat te zien voordat een andere expressie wordt ingevoerd.

S

semantiek
Semantiek is de betekenis die aan programmaconstructies wordt toegekend, waaronder hoe operatoren zich gedragen en hoe de uitvoering waarden verandert.
De semantiek van gehele deling verklaart waarom een deling die er hetzelfde uitziet in verschillende talen andere resultaten kan geven.
SQL
SQL is een taal om met relationele gegevens te werken, met query’s die gevraagde velden, rijvoorwaarden en volgorde aangeven.
SQL kan de namen opvragen van leerlingen boven een cijferdrempel en die namen alfabetisch sorteren.
stapelframe
Een stapelframe slaat informatie op die bij één actieve functieaanroep hoort, zoals lokale koppelingen en de terugkeerlocatie.
Elke onafgeronde recursieve aanroep heeft een eigen stapelframe nodig, tenzij een implementatie dat kan wegoptimaliseren.
statische typering
Statische typering controleert typecompatibiliteit vóór de uitvoering, waardoor veel ongeldige combinaties tijdens de ontwikkeling kunnen worden afgewezen.
Statische typering laat een Java-compiler tekst afwijzen die wordt gebruikt om een variabele voor gehele getallen te initialiseren voordat het programma draait.
syntactische analyse
Syntactische analyse controleert tokens aan de hand van een taalgrammatica en geeft de structuur van expressies en instructies weer.
Syntactische analyse legt vast dat de vermenigvuldiging in total = price * 3; binnen de toekenning hoort.
syntaxis
Syntaxis is de verzameling regels die bepaalt welke combinaties van tokens geldige expressies, instructies en programma’s vormen.
De syntaxis van Python vereist een dubbele punt na een if-voorwaarde en inspringing voor het bijbehorende blok.

T

taalimplementatie
Een taalimplementatie is de software die een manier biedt om programma’s die in die taal zijn geschreven te vertalen en uit te voeren.
CPython en PyPy zijn verschillende implementaties, zodat Python-code verschillende uitvoeringsstrategieën kan gebruiken.
toekenning
Een toekenning koppelt een naam of opslagdoel aan een waarde en gebruikt eerst de huidige toestand om de expressie rechts te evalueren.
De toekenning count = count + 1 leest de oude waarde van de teller voordat die wordt vervangen door de verhoogde waarde.
token
Een token is een betekenisvol stuk brontekst, zoals een sleutelwoord, identifier, letterlijke waarde, operator of leesteken.
De puntkomma in een C-toekenning is een token, hoewel die geen variabele en geen numerieke waarde benoemt.
tracetabel
Een tracetabel legt relevante variabelewaarden na uitgevoerde stappen vast, zodat het pad en de veranderende toestand kunnen worden gevolgd.
Een tracetabel voor een accumulator legt het nieuwe totaal vast nadat elk element de lus heeft doorlopen.
turingvolledigheid
Turingvolledigheid is het vermogen om elk berekenbaar algoritme uit te drukken wanneer geïdealiseerde onbeperkte tijd en geheugen beschikbaar zijn.
Turingvolledigheid vertelt een team niet of een taal de browserondersteuning of bibliotheken heeft die het project nodig heeft.
type-inferentie
Type-inferentie leidt typen af uit de omringende code, waardoor statische controles mogelijk zijn ook wanneer typeannotaties zijn weggelaten.
Type-inferentie laat een compiler een type voor gehele getallen afleiden uit een beginwaarde in plaats van uit een geschreven typeaanduiding.

U

uitvoeringsfout
Een uitvoeringsfout ontstaat wanneer een uitgevoerde bewerking volgens de taalregels of onder de beschikbare omstandigheden niet kan doorgaan.
Delen door nul in Python veroorzaakt een uitvoeringsfout wanneer de uitvoering die berekening bereikt.

V

verweesde pointer
Een verweesde pointer verwijst naar opslag die al is vrijgegeven en niet langer een geldig doel voor toegang is.
Een verweesde pointer kan nog steeds een oud adres bevatten, ook nadat dat adres voor andere gegevens is hergebruikt.
virtuele machine
Een virtuele machine is software die een uitvoeringsmodel biedt voor instructies, zoals de bytecode die een uitvoeringsomgeving van een taal gebruikt.
Een compatibele virtuele Java-machine laat hetzelfde klassebestand draaien op computers met verschillende processors.

Z

zuivere functie
Een zuivere functie geeft hetzelfde resultaat voor dezelfde argumenten en heeft naast het teruggeven van dat resultaat geen neveneffecten.
Een zuivere functie voor een vaste vermenigvuldigingsfactor kan worden getest zonder een globale toestand in te richten of uitvoer op te vangen.

Over deze les

Door StudeliFout melden

Maak je eigen Studiemateriaal

Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.

Aan de slag