Wat is de beste programmeertaal?
Profielwerkstuk Hoofdvak: Wiskunde
Matthijs Melissen Stedelijk Gymnasium Breda Klas 6B December 2003 Begeleidend docent: dhr. Martens
Inhoudsopgave Inleiding...................................................................................................................................... 4 Syntaxis....................................................................................................................................... 5 Wat is syntaxis?....................................................................................................................5 Opdrachten........................................................................................................................... 5 Conclusie....................................................................................................................... 5 Commentaar......................................................................................................................... 6 Tot einde regel............................................................................................................... 6 Van begin-commentaar tot einde-commentaar.............................................................. 6 Conclusie....................................................................................................................... 6 Variabelen.............................................................................................................................7 Datatypes........................................................................................................................7 Declareren...................................................................................................................... 8 Arrays.............................................................................................................................8 Records of structures......................................................................................................9 Conclusie....................................................................................................................... 9 Operatoren............................................................................................................................ 9 Toekenningsoperatoren................................................................................................10 Rekenkundige operatoren............................................................................................ 10 String-operatoren......................................................................................................... 10 Vergelijkingsoperatoren...............................................................................................10 Conclusie..................................................................................................................... 11 Programmavolgorde........................................................................................................... 12 Groeperen.....................................................................................................................12 Keuze........................................................................................................................... 12 Goto............................................................................................................................. 13 Onbeperkte herhaling...................................................................................................14 Herhaling van tevoren vastgesteld aantal keren...........................................................14 Herhaling met controle vooraf..................................................................................... 15 Herhaling met controle achteraf...................................................................................15 Herhaling binnen een array.......................................................................................... 15 Conclusie..................................................................................................................... 16 Programmeerparadigma’s...................................................................................................16 2
Wat zijn paradigma’s?................................................................................................. 16 Imperatief programmeren............................................................................................ 16 Procedureel programmeren.......................................................................................... 17 Object-georiënteerd programmeren............................................................................. 19 Functioneel programmeren.......................................................................................... 21 Logisch programmeren................................................................................................ 22 Conclusie..................................................................................................................... 23 Conclusie............................................................................................................................ 23 Gebruikersomgeving................................................................................................................. 24 Conclusie..................................................................................................................... 24 Toepassingsgebied.................................................................................................................... 26 Conclusie..................................................................................................................... 26 Historie......................................................................................................................................27 Hoe het begon.............................................................................................................. 27 De eerste talen..............................................................................................................27 De ‘moderne’ talen...................................................................................................... 28 De jaren ‘90..................................................................................................................29 Conclusie..................................................................................................................... 29 Conclusie...................................................................................................................................30 Bronvermelding.........................................................................................................................31
3
Inleiding Zoals al in het voorwoord vermeld, zal ik in dit werkstuk verschillende programmeertalen met elkaar vergelijken, en kijken wat de beste programmeertaal is. Programmeren is het schrijven van een aantal opdrachten in een bepaalde computertaal, die de computer vertellen wat hij precies moet doen. Zo’n computertaal wordt een programmeertaal genoemd. Er zijn enkele honderden van dit soort talen. Overigens, soms stelt men aan een programmeertaal de eis dat het programma, na eventueel gecompileerd (voor de processor leesbaar gemaakt) gemaakt te zijn, zonder speciale aanvullende software uitgevoerd kan worden. Talen waar specifieke aanvullende software voor nodig is worden scripttalen genoemd. Dit onderscheid wordt in dit werkstuk niet gemaakt: ook scripttalen worden hier onder de noemer programmeertalen geschaard. Sommige van die talen lijken erg op elkaar, terwijl andere totaal verschillen. In deze scriptie worden die onderlinge overeenkomsten en verschillen behandeld. Het gaat hierbij om de syntaxis (de regels van een programmeertaal) en de gebruikersomgeving. Verder komen de toepassingsgebieden van de talen aan bod, en ten slotte volgt de geschiedenis van programmeertalen.
4
Syntaxis Wat is syntaxis? Een programmeertaal heeft altijd specifieke regels, die met de grammaticaregels in een normale taal te vergelijken zijn. Het geheel van deze regels wordt de syntaxis of syntax van een taal genoemd. De regels in een programmeertaal zijn wel veel strikter dan die van een gewone taal: wij kunnen bij een fout tegen de grammatica meestel wel begrijpen wat er bedoeld wordt, maar een computer kan dit niet. Elke programmeertaal heeft een andere syntaxis. In dit hoofdstuk wil ik de overeenkomsten en verschillen tussen de syntaxis van verschillende talen laten zien. Verder probeer ik te onderzoeken welke taal de beste syntaxis heeft.
Opdrachten De meeste programmeertalen bestaan uit een serie opdrachten. In assemblertaal, Basic, E, Fortran, Haskell en Lua staat elke opdracht op een nieuwe regel. De opdrachten worden dus gescheiden door regeleindes. Mocht je in Basic meerdere opdrachten op een regel willen, dan scheid je die met een dubbele punt. In de vrijwel alle nieuwere programmeertalen (Ada, Awk, B, Beta, C, C#, C++, E, Haskell, Java, JavaScript, merd, Modula-3, OCaml, Perl, PHP, Pike, PL/I, Pliant, Python, Ruby, sh, SML, Tcl) worden de opdrachten gescheiden door puntkomma’s. Soms worden opdrachten gescheiden door een punt (Smalltalk), een spatie (Eiffel), of >> (Haskell). Opdrachten kunnen onderbroken worden door regeleindes (C, C++, Common Lisp, Java, JavaScript, Perl, PHP). Ze staan dan dus verspreid over twee of meer regels. In Basic kan dat ook, maar je moet dan wel een _ aan het einde van de regel, dus voor het regeleinde, zetten.
Conclusie Er zijn dus grofweg twee methodes te onderscheiden, met elk hun eigen voor- en nadelen: de eerste is dat regeleindes expliciet moeten worden aangegeven. Dit maakt opdrachten over meerdere regels mogelijk, maar het einde-opdracht-teken mag niet vergeten worden, omdat het programma dan niet werkt. De tweede methode is een opdrachteinde aangeven door een
5
nieuwe regel. Dit heeft mijn voorkeur, want nu hoeft niet elke keer aan het sluitteken gedacht te worden, hoewel een opdracht nu wel op een regel moet passen.
Commentaar Commentaar wordt gebruikt om stukken tekst in de programmacode te zetten die niet hoeven te worden uitgevoerd. Het kan gebruikt worden om informatie over de werking van delen van het programma te geven aan iemand die de code leest (of aan de programmeur zelf). Andere toepassingen van commentaar zijn de mogelijkheid informatie over bijvoorbeeld het copyright in de code te zetten, of om het programma te kunnen uitvoeren zonder bepaalde regels. Deze regels hoeven dan niet definitief te worden gewist, maar kunnen tot commentaar gemaakt worden. Er zijn twee typen commentaar: commentaar vanaf het commentaarteken tot het einde van de regel, of commentaar tussen de code begin commentaar en einde commentaar.
Tot einde regel De meest voorkomende manieren om alles tot het einde van de regel commentaar te maken zijn # (Awk, E, Icon, merd, Perl, PHP, Pliant, Python, Ruby, sh, Tcl) en // (BCPL, C#, C++, C99, Dylan, E, Java, JavaScript, PHP, Pike). Ook -- (Ada, Cecil, Eiffel, Haskell, Lua, Sather, Simula), een puntkomma (assembler, Common Lisp, Emacs Lisp, Rebol, Scheme) en een procentteken (Erlang, Mercury, Oz, Postscript, Prolog, TeX) komen veel voor. Verder wordt in Basic het woord REM gebruikt, in Visual Basic de apostrof (‘), in Forth de backslash (\), in assembler en Fortran90 het uitroepteken en in Fortran een C of een *. Bij Fortran moet het commentaarteken wel in de eerste kolom staan.
Van begin-commentaar tot einde-commentaar In de meeste talen is alles wat tussen /* en */ (B, C, C#, C++, Java, JavaScript, Mercury, PHP, Pike, PL/I, Dylan, Oz) staat commentaar. Er zijn een aantal varianten hierop, zoals (*… *) bij Beta, Modula-3, OCaml, Pascal en SML. In Pascal wordt commentaar meestal tussen accolades gezet.
Conclusie De verschillen tussen de manier waarop de verschillende talen met commentaar omgaan zijn vrij klein. Wel is het handig als een taal beide typen commentaar ondersteunt. Verder kan de methode van Pascal verwarring wekken, omdat accolades in veel andere talen voor het groeperen van opdrachten gebruikt worden. 6
Variabelen Terwijl een programma wordt uitgevoerd, worden er constant waarden opgeslagen in het geheugen. In assemblertaal gebeurt dat door de opdracht te geven een waarde, bijvoorbeeld de score in een spel, op te slaan op een bepaalde plek in het geheugen. Het was natuurlijk erg onhandig om te onthouden dat de score op bijvoorbeeld plek 13534 stond. Daarom hoefde in de latere programmeertalen een opgeslagen waarde niet meer met een getal aangesproken te worden, maar kon ook een letter gebruikt worden, bijvoorbeeld een s om de score aan te spreken. Nog weer later waren ook woorden van meerdere letters toegestaan. Zo kon de score gewoon met het woord score aangeduid worden.
Datatypes Er zijn verschillende soorten variabelen. In sommige variabelen wordt tekst opgeslagen en in andere getallen. Het type van een variabele heet een datatype. Een getal is een van de meest voorkomende datatypes. De score in het bovenstaande voorbeeld is een voorbeeld van een getal. Soms wordt dit datatype nog opgedeeld in onder andere integers, gehele getallen, en floating-pointgetallen, getallen met decimalen. Een veelvoorkomend datatype is een tekst, ook wel string of char genaamd. Een string kan in de meeste talen elke willekeurige lengte hebben. Ook strings van nul, één of duizenden tekens zijn mogelijk. Strings worden meestal genoteerd door ze tussen dubbele aanhalingstekens te zetten. Dat gebeurt in de talen Ada, Awk, C, C#, C++, Common Lisp, Dylan, Eiffel, FL, Haskell, Java, Modula-3, OCaml, Oz, Pascal, Pike, Pliant, Python en SML. Alleen Beta, Perl, Ruby, sh en Smalltalk gebruiken enkele aanhalingstekens. JavaScript, Lua, Python, PHP en Xpath ondersteunen zowel enkele als dubbele aanhalingstekens. Soms wil je een variabele in een string zetten. Je wilt bijvoorbeeld eerst de tekst ‘Je hebt’, dan de score, en dan de tekst ‘punten’. In Perl, PHP, sh en Tcl kan de variabele gewoon binnen de aanhalingstekens gezet worden. Dat is natuurlijk erg makkelijk. In merd moet de variabele tussen accolades komen. Ook in Ruby moet dat, maar er moet wel een hekje voor. In de meeste andere talen kan dit niet. Daar is het nodig eerst de aanhalingstekens te sluiten en een opdracht geven om twee strings te verbinden. Is sommige talen bestaat er een apart datatype voor een string van een teken. Dit wordt een character of char genoemd. Meestal (in Ada, B, C, C#, C++, Eiffel, Haskell, OCaml, Pike) wordt een waarde tussen enkele aanhalingstekens gezet om aan te geven dat het een char is. 7
Alleen merd gebruikt hiervoor dubbele aanhalingstekens. Er zijn ook nog andere mogelijkheden, bijvoorbeeld een vraagteken vóór het karakter in Emacs Lisp en Ruby. Een boolean is een datatype dat slechts twee waarden kan aannemen: waar (true) en niet waar of onwaar (false). Niet alle programmeertalen kennen dit. Dan worden er vaak de 0 (onwaar) en 1 (waar) of 0 (waar) en -1 (onwaar) voor gebruikt.
Declareren Maar hoe weet de programmeertaal nu welk datatype een variabele is? In Fortran is het eenvoudig: de variabelen waarvan de namen met de letters i tot en met n beginnen, waren gehele getallen. De rest zijn decimale getallen. In Basic moeten namen van gehele getallen eindigen op het procentteken, strings eindigen op een dollarteken en de rest is van het type floating-point. In de nieuwere talen komen er steeds meer datatypes. Het is dus onmogelijk geworden om het datatype aan de hand van de naam te onderscheiden. Daarom moet expliciet worden verteld van welk datatype een variabele is. Dit heet declareren. In Pascal moeten de declaraties voor de opdrachten staan. In nieuwere talen mogen ze ook tussen de opdrachten staan. De wijze waarop een variabele gedeclareerd moet worden is erg wisselend per programmeertaal. Meestal wordt een codewoord als let of var gebruikt. Declareren is aan de ene kant erg lastig: voor elke variabele die de programmeur wil gebruiken, is een declaratie nodig. Aan de andere kant is het ook nuttig, want het helpt fouten voorkomen: als een variabele als string gedefinieerd is, geeft het programma een foutmelding als er geprobeerd wordt een getal bij op te tellen.
Arrays In sommige spellen die over internet gespeeld worden kunnen tientallen spelers meedoen. Al die spelers hebben natuurlijk een eigen score. Het zou erg onhandig zijn om een scorespeler1, een scorespeler2, een scorespeler3, enzovoorts allemaal apart te moeten declareren. Bovendien moeten ze bijvoorbeeld bij het begin van het spel allemaal apart op nul worden gezet. Voor dit soort toepassingen gebruikt men normaal gesproken array’s. Een array is een variabele die meerdere waarden tegelijk kan bevatten. Zo kan hier bijvoorbeeld de array score gebruikt worden, die de scores van alle spelers bevat. De afzonderlijke variabelen, elementen genaamd, kunnen worden aangesproken met bijvoorbeeld score[1] voor speler 1. Deze methode wordt gebruikt in B, C, C#, C++, Dylan, E, Java, JavaScript, Lua, merd, 8
Modula-3, Pascal, Perl, PHP, Pike, Python en Ruby. Er zijn een paar talen die afwijken, zoals Ada met de syntaxis score(1), gewone haken in plaats van rechte haken. Het datatype is een eigenschap van de array en niet van de elementen. Alle elementen van een array hebben daardoor hetzelfde datatype. De elementen worden in de meeste talen tussen rechte haken gezet, gescheiden door komma’s (E, Haskell, JavaScript, merd, Perl, Postscript, Prolog, Python, Ruby, SML). Alleen C, C++ en Lua gebruiken accolades. Lisp gebruikt ronde haken, maar voor de eerste haak moet een enkele aanhalingsteken.
Records of structures In Pascal kent men ook records. Dit is te vergelijken met een record uit een database. Het record kent verschillende velden, die elk een ander datatype kunnen hebben. Zo kan van een persoon de voornaam, achternaam en leeftijd worden bijgehouden in één record. Ook in C is dit mogelijk, maar daar wordt het een struct of structure genoemd. Een element van een record of structure wordt in vrijwel alle talen benaderd met een punt tussen de naam van de structure en de naam van het element, bijvoorbeeld persoon1.voornaam.
Conclusie Er zijn maar weinig fundamentele verschillen tussen de manier waarop programmeertalen met variabelen omgaan. Enkele datatypen extra maakt weinig uit, of gewone haken of rechte haken. Wel erg nuttig is de mogelijkheid om zelf datatypen de kunnen definiëren. In de paragraag over object-georiënteerd programmeren wordt daar nader op in gegaan. Wel een belangrijk punt is of declareren verplicht is. Of dat een voordeel is, hangt af van de grootte van het programma. Er is extra aandacht en moeite voor nodig, maar het maakt het programma wel een stuk duidelijker
Operatoren Operatoren zijn symbolen om bewerkingen met variabelen uit te voeren. Zo is het mogelijk variabelen een waarde te geven, er rekenkundige bewerkingen mee uit te voeren en ze te vergelijken.
9
Toekenningsoperatoren Een toekenningsoperator wordt gebruikt om een waarde aan een variabele te geven. Meestal gebeurt dit met het teken = (Awk, B, Basic, C, C#, C++, Erlang, Icon, Java, JavaScript, Lua, Oz, Perl, PHP, Pike, s): met a = 1 krijgt de variabele a de waarde 1. Soms wordt := gebruikt, om verwarring te voorkomen met = in de betekenis van het vergelijken van twee waarden (Ada, BCPL, Cecil, Dylan, E, Eiffel, Modula-3, Pascal, Pliant, Sather, Simula, Smalltalk). In dat geval wordt a = 1 gebruikt om te kijken of a gelijk aan 1 is, en a := 1 om a de waarde 1 te geven. Andere vormen die voorkomen zijn <- (OCaml), : (BCPL, Rebol), def (E, Postscript), setq (Emacs Lisp) en setf / setq / set (Common Lisp).
Rekenkundige operatoren Praktisch alle programmeertalen kennen de rekenkundige operators + (plus), - (min), * (keer) en / (gedeeld door). Ze worden op dezelfde manier als in de wiskunde gebruikt. Voor negatieve getallen wordt meestal een min gebruikt. Alleen Oz gebruikt een tilde. De meeste talen kennen ook bepaalde functies voor bijvoorbeeld worteltrekken, logaritmes en goniometrie.
String-operatoren Het is in alle talen mogelijk twee strings samen te voegen. In de meeste talen (C#, C++, E, Eiffel, Java, JavaScript, merd, Pascal, Pike, Pliant, Python, Ruby) gaat dat met een plus-teken. In Perl en PHP wordt de punt hiervoor gebruikt, in Smalltalk de komma, in Lua twee punten, in Ada, Modula-3 en Visual Basic de &, in OCaml, en SML de ^, in Cecil, Icon en PL/I || en in Haskell ++. In TCL kunnen de strings gewoon achter elkaar worden gezet.
Vergelijkingsoperatoren Voorwaarden worden gebruikt om als een variabele een bepaalde waarde heeft het ene te doen, en als die variabele een andere waarde heeft iets anders te doen. Een voorwaarde kan waar (true) of onwaar (false) zijn. Stel, de variabele a is 1. De voorwaarde a > 3 is dan onwaar, en de voorwaarde a < 3 waar. In bijna alle talen komen voorwaarden tussen haakjes. Soms is dat verplicht, soms is het facultatief. Zoals we net zagen, is het mogelijk om te kijken of een variabele groter of kleiner dan een andere variabele is. Dit gebeurt bijna altijd met de wiskundige tekens < (kleiner dan) en > (groter dan). Kleiner of gelijk aan is <= en groter of gelijk aan is >=. Alleen Mercury en Oz 10
gebruiken voor kleiner of gelijk aan =<, om verwarring met een pijltje te voorkomen. Perl gebruikt lt (kleiner dan), gt (groter dan), le (kleiner of gelijk) en ge (groter of gelijk). Fortran gebruikt .LT., .GT., .LE. en .GE.. Het is ook mogelijk om te kijken of variabele a gelijk is aan b, of ongelijk aan b. Het eerste wordt meestal gedaan met = (Eiffel, Fortran90, Pliant, Modula-2, Modula-3, BCPL, E, merd, Python, Ruby) of met == (Awk, B, C, C++, Java, OCaml, Perl, Pike, Tcl, PHP, JavaScript, Lua, Smalltalk, Dylan), om verwarring te voorkomen tussen ‘is a gelijk aan B?’ en ‘maak a gelijk aan B’. Voor ‘is niet gelijk aan’ wordt meestal != gebruikt (Awk, B, C, C++, Java, OCaml, Perl, Pike, Tcl, PHP, JavaScript). Andere mogelijkheden zijn /= (Fortran), <> (Pliant, Beta, OCaml, SML, Visual Basic), # (Modula-2, Modula-3), ~= (Lua, BCPL), ~~ (Smalltalk), ~== (Dylan), /= (Haskell, Ada) en \= (Oz). JavaScript kent ook === en !== om variabelen waarvan de datatypes verschillen te vergelijken. Normaal is dat namelijk niet mogelijk: het is onzinnig om te kijken of 5 groter is dan a. PHP kent ook ===, maar geen !==. Je kunt ook voorwaarden omkeren. Zo kun je controleren of ze juist niet waar zijn. Dat gebeurt met het woord not in Ada, Beta, Common Lisp, Eiffel, Emacs Lisp, Forth, Haskell, Lua, merd, OCaml, Perl, Pliant, Python, Smalltalk, SML en Xpath, en met een uitroepteken in Awk, B, C, C#, C++, Java, JavaScript, Perl, PHP, Pike, Ruby, Tcl. De talen BCPL, Dylan en PL/I gebruiken een tilde (~). Om meerdere voorwaarden te controleren, gebruik je ‘en’ en ‘of’. Meestal is en && en of || (Awk, C, C#, C++, E, Haskell, Java, JavaScript, merd, OCaml, Perl, PHP, Pike, Ruby, Tcl). B, BCPL en Dylan gebruiken een enkele & en |. Ook de woorden or en and komen veel voor. Ze worden gebruikt door Common Lisp, Emacs Lisp, Lua, Modula-2, Perl, PHP, Pliant, Postscript, Python, Ruby, Smalltalk, Ada, Beta, Eiffel, Forth, Pascal, SML en Xpath. OCaml is inconsequent en gebruikt or en &.
Conclusie Het gebruik van operatoren is in de meeste gevallen vrij logisch. De meeste operatoren zijn overgenomen uit de wiskunde, en daarom ook gelijk in de meeste talen. De talen die andere symbolen dan de wiskundige gebruiken, werken daardoor erg onprettig. Wel een punt is het gebruik van het teken. Dit betekent zowel ‘is gelijk aan’ als ‘wordt’, en computers zijn niet blij met dubbelzinnige symbolen. Daarom kiezen sommige talen ervoor
11
om = als ‘wordt’ te interpreteren, en andere als ‘is gelijk aan’. De ene keuze is echter niet beter te noemen als de andere,
Programmavolgorde Groeperen Vaak moet als de voorwaarde waar is niet één opdracht, maar meerdere opdrachten uitgevoerd worden. Die opdrachten moeten daartoe gegroepeerd worden, zodat ze als het ware als één opdracht beschouwd worden. Dit groeperen gebeurt meestal met accolades (C, C++, Haskell, Java, JavaScript, Perl, PHP). In veel andere talen, bijvoorbeeld Pascal, gebeurt dit met de codewoorden begin en end.
Keuze In het vorige hoofdstuk is al uitgelegd dat voorwaarden gebruikt kunnen worden om een reeks opdrachten voorwaardelijk uit te voeren. In dit hoofdstuk wordt daar verder op in gegaan. De simpelste keuzeopdracht is if-then. Hiermee wordt een opdracht (of een aantal gegroepeerde opdrachten) alleen uitgevoerd als een voorwaarde waar is. De simpelste vorm van een if-then-constructie is: if C then B
merd, OCaml, Pascal
In dit geval wordt de opdracht B alleen uitgevoerd indien C waar is. Een andere vorm is: if C do B
BCPL
Bij sommige talen die geen groeperen kennen is het noodzakelijk een opdracht te geven die aangeeft dat de opdrachten vanaf dat punt weer onvoorwaardelijk uit moeten worden gevoerd: if C then B end
Eiffel, Lua, Oz, Ruby
IF C THEN B END
Modula-2, Modula-3
if C then B end if
Ada
Soms komt de voorwaarde verplicht tussen haakjes. if (C) then B end
Dylan
In de meeste nieuwere talen is de then-opdracht helemaal verdwenen, omdat logisch is dat na if het codewoord then volgt. if (C) B
Awk, B, C, C#, C++, Java, JavaScript, PHP, Pike
if C B
Pliant
if (C) {B}
Perl
12
Soms is de omgekeerde vorm, de opdrachten vóór de voorwaarde, ook toegestaan: B if C
Perl, Ruby
Vreemdere vormen zijn: if C: B
Python
if C ?then? B
Tcl
C -> B
FL
C if b1 then
Forth
<xsl:if test="C">B
XSLT
C ifTrue: B
Smalltalk
(if C B)
Common Lisp, Scheme
Soms is het nodig om niet alleen bepaalde opdrachten uit te voeren als een voorwaarde waar is, maar ook andere opdrachten uit te voeren als de voorwaarde niet waar is. Dit kan met een if-then-else-constructie. Meestal is de constructie hetzelfde als de if-then constructie. Het enige verschil is dat er nu het codewoord else staat achter de voorwaarden die uitgevoerd worden, als de constructie wel waar is. Dit wordt gevolgd door de opdrachten die uitgevoerd moeten worden als de voorwaarde niet waar is. De meest voorkomende vorm is: if (voorwaarde) opdrachten 1 else opdrachten2 Dit werkt in Awk, B, C, C#, C++, Java, JavaScript en Pike. Als de voorwaarde waar is, wordt opdrachten1 uitgevoerd, als de voorwaarde niet waar is, wordt opdrachten2 uitgevoerd. Er zijn in andere talen wat variaties. Zo mogen de haakjes soms weggelaten worden, komt het woord then soms tussen de voorwaarde en de opdrachten en komt er soms een woord endif op het einde.
Goto De oudste opdracht om de opdrachten niet op volgorde uit te hoeven voeren is goto. Met deze opdracht wordt naar een bepaald regelnummer gesprongen. Een label is een regel met daarop een woord dat de naam van de regel aangeeft.
13
Deze opdracht is in de meeste talen verdwenen, omdat het gebruik ervan leidt tot een onoverzichtelijk programma: je kunt niet in een oogopslag zien in welke volgorde het programma wordt uitgevoerd. De opdracht ziet er in alle talen die hem gebruiken hetzelfde uit: Goto
Ada, B, Basic, BCPL, C, C#, C++, Cobol, Fortran, Perl
Onbeperkte herhaling Het is in sommige talen mogelijk een of meerdere opdrachten een oneindig aantal keren uit te laten voeren. In principe zal zo’n programma dus nooit stoppen. In merd en Ruby gaat dit met de opdracht Loop. In Ada ook, maar hier moeten de te herhalen opdrachten worden afgesloten met de opdracht End loop, en in Modula-3 met End. In talen die geen opdracht voor onbeperkte herhaling kennen, kan dit bereikt worden door een herhaling met controle vooraf te gebruiken, die nooit waar zal worden. Je kunt bijvoorbeeld herhalen tot 1 = 2. Omdat 1 nooit 2 is, zal de lus altijd doorlopen blijven worden.
Herhaling van tevoren vastgesteld aantal keren Sommige talen kennen een speciale mogelijkheid om één of meerdere opdrachten een van tevoren vastgesteld aantal keren te laten herhalen. Dit gebeurt met een speciale tellervariabele, die het aantal keren dat er herhaald is telt. In Basic gaat dat als volgt: FOR i = a TO b STEP c opdracht(en) Hier is i de teller-variabele, die loopt van waarde a tot waarde b, met stapjes van c: je kunt ook 2 – 4 – 6 – 8 – 10 tellen. In veel nieuwe talen (Awk, C, C#, C++, Java, JavaScript, Perl, PHP, Pike, Tcl) is de syntaxis als volgt: for (initiatie; voorwaarde; ophoging) opdracht(en) Eerst wordt de initiatie-opdracht uitgevoerd. Dit kan bijvoorbeeld zijn: geef i waarde 0. Als de voorwaarde vervolgens waar is, worden de opdracht(en) uitgevoerd. Daarna wordt de opdracht ophoging uitgevoerd, bijvoorbeeld door bij i de waarde 1 op te tellen. Vervolgens wordt weer gecontroleerd of volgorde waar is. Zodra de voorwaarde niet meer waar is, wordt gestopt met deze lus. Dit is een vrij belangrijke uitbreiding ten opzicht van Basic, want nu hoeft de ophoging niet perse lineair te zijn. Het is bijvoorbeeld ook mogelijk de tellervariabele elke lus te vermenigvuldigen met een getal. 14
Ook bij dit commando kan opdracht(en) een losse opdracht of meerdere gegroepeerde opdrachten zijn.
Herhaling met controle vooraf Bij deze herhaling worden een of meerdere opdrachten net zo lang herhaald als een voorwaarde waar is. Meestal gebeurt dat als volgt: while voorwaarde opdracht(en) Hier worden de opdrachten uitgevoerd zolang de voorwaarde waar is. Om precies te zijn: eerst wordt de voorwaarde gecontroleerd. Is deze waar, dan worden de opdrachten uitgevoerd. Vervolgens wordt de voorwaarde opnieuw gecontroleerd. Zodra de voorwaarde bij een controle niet waar is, dan gaat het programma verder met alles wat er na dit stuk code komt.
Herhaling met controle achteraf Bij deze herhaling worden een of meerdere opdrachten net zo lang herhaald totdat of zolang (afhankelijk van de taal) de voorwaarde waar is, maar de opdrachten worden altijd eerst één keer uitgevoerd. In C, C++, Java en Perl gebeurt dat als volgt: do opdrachten until voorwaarde Hier worden de opdrachten uitgevoerd totdat of zolang de voorwaarde waar is. Om precies te zijn: eerst worden de opdrachten uitgevoerd. Dan wordt de voorwaarde gecontroleerd. Is deze niet waar, dan worden de opdrachten opnieuw uitgevoerd. Vervolgens wordt de voorwaarde opnieuw gecontroleerd. Zodra de voorwaarde bij een controle waar is, dan gaat het programma verder met alles wat er na dit stuk code komt. In Awk, C#, JavaScript, Pike, en Ruby is het precies andersom: zolang de voorwaarde waar is, wordt de lus doorlopen, maar als hij een keer niet waar is stopt de lus. Voorbeeld: do opdrachten while (voorwaarde)
Herhaling binnen een array Er is in veel talen een speciale lus die alle elementen van een array doorloopt. De syntaxis is verschillend. Merd, Pliant en Ruby gebruiken het codewoord each. E, JavaScript, Python en Ruby gebruiken for in. Lua, Perl, Pike, PHP en Tcl kennen het woord foreach, C++ heeft for_each en Scheme for-each. Het codewoord forall wordt door Postscript en Oz gebruikt, do door Smalltalk en do_all door Eiffel.
15
De lus begint altijd met het codewoord, gevolgd door gegroepeerde opdrachten die een bepaalde bewerking op het element van de array uitvoeren.
Conclusie Ook in dit hoofdstuk zijn er niet echt schokkende verschillen tussen de talen. Er zijn wel kleine afwijkingen in de precieze syntaxis, maar de concepten zijn hetzelfde. Hoe opdrachten gegroepeerd worden, is wel een vrij belangrijk verschil. In sommige talen moet hier voor altijd een begin-end-constructie voor gebruikt worden. Het gebruik van bijvoorbeeld accolades is hier efficiënter.
Programmeerparadigma’s Wat zijn paradigma’s? Er zijn verschillende typen programmeertalen, die elk gebruik maken van andere concepten en een andere manier vragen om tegen een probleem aan te kijken. Die verschillende typen talen heten paradigma’s. In figuur 1 staan de verschillende paradigma’s schematisch
Figuur 1
weergegeven. Het meest voorkomende paradigma is het imperatieve paradigma. Een onderdeel daarvan is het procedurele paradigma. Daarvan is object-georiënteerd programmeren weer een onderdeel. Bijna alle voorbeelden in voorgaande paragrafen komen uit imperatieve talen. Naast het imperatieve paradigma staat het declaratieve paradigma. In tegenstelling tot imperatieve programmeertalen geef je bij functionele programmeertalen geen serie opdrachten, maar het doel dat je wilt bereiken, met een serie definities. Er zijn twee types declaratieve programmeertalen: functionele en logische. In de volgende paragrafen worden de verschillende paradigma’s besproken.
Imperatief programmeren Imperatief betekent bevelend: bij dit type programmeertalen geven we bevelen aan de processor. De computer voert de bevelen, ook wel opdrachten of instructies genoemd, in de programmacode van boven naar beneden uit. Wel is het mogelijk om gedeelten code meerdere 16
keren uit te voeren, delen voorwaardelijk uit te voeren of de opdracht te geven naar een andere regel te springen. De meeste programmeertalen zijn imperatief.
Procedureel programmeren Het procedurele programmeerparadigma is een uitbreiding op het imperatieve. De eigenschappen van puur imperatieve talen zijn dus ook van toepassing op procedurele talen. Het procedurele paradigma is er op gebaseerd dat in een programma een aantal taken vaak veelvuldig uitgevoerd moeten worden. Daarom wordt er van zo’n taak die vaker uitgevoerd moet worden een deelprogramma gemaakt. De naam van zo’n deelprogramma verschilt per programmeeraal. In assembler, Fortran en Basic heet het bijvoorbeeld een subroutine, in Algol en Pascal een procedure, in C en PHP een functie en in C++ en Java een methode. Het deelprogramma kan zo vaak als nodig is aangeroepen worden door het hoofdprogramma. Het aanroepen van een deelprogramma heet een call of functieaanroep. In C en Java wordt het hoofdprogramma zelf ook beschouwd als een functie, genaamd main. Het starten van het programma staat dan gelijk aan het aanroepen van de main-functie. Het komt vaak voor dat een taak meerdere keren uitgevoerd moet worden, maar telkens op een net iets andere manier. Het kan bijvoorbeeld voorkomen dat er 7 cirkels getekend moeten worden, maar wel met 7 verschillende kleuren en met 7 andere kleuren opgevuld. Ook hier kan gebruik worden gemaakt van deelprogramma’s. In zo’n geval wordt bij de functieaanroep opgegeven welke kleur de cirkelomtrek moet hebben, en met welke kleur de cirkel ingekleurd moet worden. De waarden die met de functieaanroep mee worden gegeven, in dit geval omtrekkleur en invulkleur, worden parameters of argumenten genoemd. De functieaanroep gebeurt meestal met de naam van de functie, gevolgd door de parameters gescheiden door komma’s tussen komma’s: tekencirkel(rood, groen) (Ada, Awk, Awk, B, C, C#, C++, Dylan, E, Eiffel, Java, JavaScript, Lua, Mercury, merd, Modula-3, Pascal, Perl, PHP, Pike, Python, Ruby, Xpath) De haakjes en komma’s ontbreken bij Haskell, OCaml, Pliant, SML, Tcl: tekencirkel rood groen Bij Scheme, Oz en Lisp komt alles tussen haakjes: (tekencirkel rood groen) Bij Forth, Postscript en Smalltalk is de syntaxis andersom: rood tekencirkel FL en Pliant gebruiken een dubbele punt: tekencirkel: rood 17
In Haskell, OCaml, BCPL en merd, overigens geen procedurele maar functionele talen, wordt een functie meer op een wiskundige manier aangegeven. In Haskell gebeurt dat bijvoorbeeld op de volgende wijze: tekencirkel rood groen = … Eerst komt de naam van de functie, dan de naam van de argumenten en dan, in dit voorbeeld op de plaats van de puntjes, de functie zelf. De functie zelf wordt ook met een codewoord aangegeven. Meestal komt eerst het codewoord, vervolgens de functienaam, dan de parameters (meestal tussen haakjes) en dan de gegroepeerde opdrachten. In Perl is het codewoord sub. In Python en Ruby is het def. In Awk, Visual Basic, JavaScript, Ada en Pascal wordt het woord function gebruikt. Soms is het handig als een deelprogramma een resultaat terug geeft aan het hoofdprogramma. Je kan bijvoorbeeld honderd keer een wortel uit moeten rekenen en daar een deelprogramma voor schrijven, maar daar heb je niets aan als het deelprogramma de waarde niet aan het hoofdprogramma door geeft. Het teruggeven van een waarde gebeurt in praktisch alle talen met de opdracht return, gevolgd door de variabele die teruggegeven moet worden. In sommige talen is de naam van een deelprogramma dat geen waarde teruggeeft en een die dat wel doet verschillend. Zo heet het eerste in Basic een subroutine en in Pascal een procedure, maar heet het tweede in beide talen een functie. Deelprogramma’s kunnen ook elkaar aanroepen. Zo kan het hoofdprogramma een functie aanroepen die een vierkant tekent, en die functie kan op zijn beurt weer een functie die een lijn tekent aanroepen. Een functie kan zelfs zichzelf aanroepen! Dat heet recursie. Er moet dan wel voor gezorgd worden dat de functie zich maar een beperkt aantal keer aanroept. Variabelen die gedeclareerd worden in het deelprogramma, kunnen meestal alleen aangesproken worden binnen dat deelprogramma en variabelen uit het hoofdprogramma zijn niet binnen de deelprogramma’s de lezen of te wijzigen. Je kunt in de meeste talen wel variabelen declareren die in alle functies te lezen en wijzigen zijn: globale variabelen. Bij procedurele talen kan ook gebruik worden gemaakt van zogenaamde abstracte datatypes (ADT). Deze bestaat altijd twee delen. Het eerste deel is een structure. Het tweede deel zijn
18
functies die als eerste parameter de structure hebben, en dus de structure bewerken. Men kan nu nieuwe variabelen hiervan maken. De structure moet bewerkt worden via de functie, en niet rechtstreeks. Het voordeel van deze werkwijze is dat de programmeur die het hoofdprogramma schrijft niet hoeft te weten hoe de functies precies werken, alleen hoe hij ze moet gebruiken. Stel, je hebt een spel waaraan meerdere spelers mee kunnen doen. Je kunt nu een abstract datatype speler aanmaken. Je maakt vervolgens net zoveel variabelen hiervan als er spelers zijn. Wil je nu bijvoorbeeld 100 punten geven aan speler 5, dan hoef je niet te weten waar de puntentelling wordt bijgehouden, maar je kunt gewoon bijvoorbeeld de functie geefpunten, die al door iemand anders gemaakt is, aanroepen: geefpunten(speler[5], 100)
Object-georiënteerd programmeren Object-georiënteerd programmeren of object orientated programming (OOP) is een onderdeel van het procedurele paradigma. Het vervangt abstracte datatypes. Abstracte datatypen heten nu klassen of classes. De functies heten nu methoden. Een klasse valt te beschouwen als een blauwdruk van een object: van een klasse kunnen een aantal objecten worden gemaakt, net als er een aantal variabelen van een bepaald datatype gemaakt kunnen worden. Het aanmaken van een nieuwe klasse gebeurt in bijna alle talen met de opdracht new. Het verschil met abstracte datatypes is dat de methodes nu officieel bij de klasse horen. Het datatype als eerste parameter ontbreekt nu dus. De attributen mogen binnen de methode direct worden aangesproken. Daarbuiten kan dat niet, om de overzichtelijkheid te bewaren. Een klasse begint meestal met het woord class (C#, C++, Haskell, Java, MzScheme, OCaml, PHP, Pike, Python, Ruby), gevolgd door de methoden en variabelen, meestal binnen accolades. Er zijn vergelijkbare codewoorden in andere talen, zoals Defclass in Common Lisp, en Subclass in Smalltalk. Om een methode van een andere klasse aan te spreken, geef je eerst de naam van de klasse, gevolgd door de methode. Meestal zijn de klasse- en de methodenaam gescheiden door een punt (Beta, C#, C++, Cecil, Delphi-Kylix, Eiffel, Icon, Java, JavaScript, merd, Modula-3, Python, Ruby, Sather, Visual Basic). Alleen OCaml gebruikt een hekje, Lua een dubbele punt, Pliant en Tcl een spatie, E een pijltje (<-) en C++, Perl, PHP en Pike een pijltje de andere kant op (->). Het is mogelijk een klasse te maken die een uitbreiding (extensie) is op een klasse. Dit heet een subklasse. In de nieuwe klasse kunnen alle attributen en methodes van de oude klasse
19
gebruikt worden. Dit heet overerving of inheritence. Voordeel hiervan is dat wijzigingen in de hoofdklasse meteen worden overgenomen in de subklasse. In C++ is het mogelijk dat een subklasse meerdere superklassen heeft. Dit lijkt handig, maar het lijdt tot problemen als een klasse twee superklasses heeft die zelf weer een gemeenschappelijke superklasse hebben. Erft de subklasse nu elke methode van de gemeenschappelijke klasse een- of tweemaal? Om dit soort problemen te vermijden is in Java maar een superklasse toegestaan. Een klasse die een uitbreiding is op een andere klasse wordt op steeds verschillende manieren gemaakt. class nieuwe_klasse extends oude_klasse (Java) class nieuwe_klasse : oude_klasse (C++) class nieuwe_klasse(oude_klasse): (Python) class nieuwe_klasse < oude_klasse end (Ruby) class nieuwe_klasse inherit oude_klasse end (Eiffel) class nieuwe_klasse :< oude_klasse (Beta) parent oude_klasse: nieuwe_klasse (Smalltalk) Attributen en methoden kunnen voor alle klassen toegankelijk zijn (public), of alleen voor andere methoden binnen dezelfde klasse (private). In C++ mogen ook subklassen privateattributen en -methoden gebruiken. In Java mag dat niet, behalve als er in plaats van private een derde type, protected, wordt gebruikt. Als meerdere programmeurs aan een programma werken, of als een programma erg groot wordt, kan het lastig worden het overzicht van een programma te houden. De kans is dan erg groot, dat per ongeluk twee functies dezelfde naam krijgen, en dat leidt uiteraard tot foutmeldingen omdat het programma niet weet welk van de twee functies bedoeld wordt. Dit wordt in de objectgeoriënteerde talen opgelost door functies in klassen te plaatsen. In Java kun je zelfs klassen weer onderbrengen in zogenaamde packages. Nadeel is wel dat ook bij eenvoudige programma’s gebruik van klassen gemaakt moet worden. Over het algemeen wordt de inmiddels in onbruik geraakte taal Simula uit 1975 als eerste OOP-taal beschouwd, maar ADT was al langer bekend. Ook Smalltalk uit 1980 was een vroege taal van dit concept. 20
OOP is vooral populair sinds grafische systemen met vensters, omdat die makkelijk als objecten met verschillende eigenschappen (bijvoorbeeld de kleur van het venster) en methoden (bijvoorbeeld het maximaliseren ervan) gezien kunnen worden. Belangrijke OOPtalen zijn C++ uit 1985, een object-georienteerde uitbreiding op C, en Java uit 1995.
Functioneel programmeren Het functionele paradigma maakt onderdeel uit van het declaratieve. Er zijn dus een aantal definities, in plaats van een aantal opdrachten. Bij functionele programmeertalen zijn die definities een aantal functies. Voorbeelden hiervan zijn Lisp, Haskell, Miranda en Hope. Een belangrijk concept in functionele talen zijn atomen (atoms) en lijsten (lists). Een atoom kan zowel een variabele zijn als een getal. Een lijst is een opeenvolging van atomen en is enigszins te vergelijken met een array in andere talen. Een lijst kan overigens ook leeg zijn. Ook lijsten staan in Lisp altijd tussen haakjes. Als een functioneel programma gestart wordt, worden eerst de atomen geëvalueerd, en vervolgens de lijsten. Het evalueren van atomen komt er op neer, dat geprobeerd wordt de naam van de variabele te vervangen door de waarde ervan. Het evalueren van lijsten is wat ingewikkelder. Als een lijst geëvalueerd wordt, beschouwt het programma de lijst als een functie, met het eerste element van de lijst als functienaam en de volgende elementen als argumenten. Er bestaan ingebouwde functies, maar het is ook mogelijk functies zelf te definiëren. Een van de ingebouwde functies van Lisp is de functie +, om op te tellen. Om de getallen 1 tot en met 5 bij elkaar op te tellen gebruik je dus: (+ 1 2 3 4 5) Het is ook mogelijk functies te nesten. We bekijken het volgende Lisp-programma: (+ 1 2 (* 3 a)) Eerst wordt nu het atoom a geëvalueerd. Laten we even aannemen dat a de waarde 4 heeft. Dan worden 3 en 4 met elkaar vermenigvuldigd. Vervolgens worden 1, 2 en de uitkomst van de geneste functie, 12 dus, bij elkaar opgeteld. Soms is het niet de bedoeling dat een atoom of lijst geëvalueerd wordt. Dat kan het geval zijn als met a niet de waarde van a, maar de letter a bedoeld wordt, of als met (optellen aftrekken vermenigvuldigen delen) niet het optellen van de waarden van ‘aftrekken, vermenigvuldigen en delen’ bedoeld wordt, maar een lijst met de namen van vier rekenkundige bewerkingen. Dit kan opgelost worden door een enkel aanhalingsteken voor het atoom of de lijst te zetten. Hiermee wordt dit niet geëvalueerd.
21
Er is in Lisp ook een functie voor lussen. Dat is de functie if. De argumenten van deze functie zijn achtereenvolgens de voorwaarde, de functie die uitgevoerd wordt als de voorwaarde waar is en de functie die uitgevoerd wordt als de functie onwaar is. Het volgende programma berekent dus het positieve verschil tussen x en y: (if (> x y) (- x y) (- y x)) Hier staat namelijk dat als x groter dan y is, y van x afgetrokken moet worden. Zo niet, dan moet y van x afgetrokken worden. Ook lussen zijn in een functionele programmeertaal mogelijk. Dit gebeurt door een functie zichzelf een aantal maal te laten aanroepen.
Logisch programmeren Een procedureel algoritme is ‘Zoek eerst een K met een deur van de keuken naar K. Zoek vervolgens een D dat zich in K bevind’. In logische vorm zou je iets krijgen als ‘Zoek een T en R waarvoor geldt dat er een deur van de keuken naar R is en T zich in R bevindt’. Bij logische programmeertalen worden er een aantal zogenaamde predikaten gedefinieerd. Een predikaat kan een feit (fact) of regel (rule) zijn. Deze kennen we ook in de logica. Denk aan het bekende voorbeeld ‘Socrates is een mens. Alle mensen zijn sterfelijk. Conclusie: Socrates is sterfelijk.’. In dit voorbeeld is ‘Socrates is een mens’ een feit en ‘alle mensen zijn sterfelijk’ een regel. In Prolog ziet dit programma er bijvoorbeeld als volgt uit: mens(socrates). sterfelijk(X) :- mens(X). Hier is socrates een waarde en X is een variabele. Zoals te zien is, worden waarden met een kleine letter en variabelen met een hoofdletter geschreven. Variabelen zijn alleen beschikbaar binnen één predikaat. Een logisch programma wordt gestart met een zoekvraag (query). In dit voorbeeld kunnen we bijvoorbeeld vragen wie er sterfelijk zijn met de opdracht sterfelijk(X). Prolog zal ‘socrates’ als antwoord geven. In een logisch programma worden feiten gebruikt om data in het programma op te nemen, terwijl dat in een imperatief programma vaak een database is. Het is ook mogelijk meerdere voorwaarden in een zoekvraag uit te voeren. Stel dat je wilt weten of iets eetbaar is en zich in de keuken bevindt, dan wordt het eerste voedsel bekeken en uitgezocht of dit eetbaar is. Zo nee, dan wordt het volgende voedsel bekeken, zo ja, dan wordt
22
bekeken of het zich in de keuken bevindt. Zo nee, dan wordt opnieuw bekeken of het volgende voedsel zich in de keuken bevindt (dit ‘stapje terug doen’ heet backtracking), zo ja, dan wordt de naam naar het scherm geschreven. Dit systeem vervangt het systeem van lussen uit imperatieve programmeertalen. Functies worden vervangen door regels. Voorbeelden van logische talen zijn Prolog, Parlog en Trilogy.
Conclusie De verschillen tussen de in dit hoofdstuk besproken paradigma’s zijn erg groot. Als je gewend bent aan een bepaald concept, is het lastig om een ander te leren. Talen binnen een paradigma lijken wel sterk op elkaar. Omdat de paradigma’s onderling zo totaal verschillend zijn, is het moeilijk ze te vergelijken, laat staan een beste aan te wijzen. Wat het beste paradigma is voor een bepaalde programmeur is, is ook afhankelijk van zijn achtergrond. Een logicus zal sneller een logische taal kunnen leren, terwijl functionele talen meer bij de wiskundige manier van denken aansluiten. Het object-georiënteerde paradigma is op het moment het verst ontwikkeld, dus de beste talen zijn momenteel waarschijnlijk van dit type.
Conclusie Ik heb nu de belangrijkste aspecten van de syntaxis van programmeertalen besproken. Er blijkt dat de verschillen in syntaxis tussen de verschillende talen zijn kleiner dan op het eerste gezicht lijkt. Er zijn wel kleine verschillen in welke codewoorden en leestekens precies gebruikt worden, maar de structuur van de meeste talen is hetzelfde. Een uitzondering hierop vormen de niet-imperatieve talen: deze zijn heel anders opgebouwd. Er valt echter geen conclusie te trekken of deze talen beter zijn dan de imperatieve, of welk paradigma het beste is.
23
Gebruikersomgeving Voor het schrijven van een computerprogramma zijn verschillende onderdelen nodig. Een eerste vereiste is natuurlijk een text editor, om de programmacode in te typen. Verder is er een compiler of interpreter nodig, of beide. Een compiler is een computerprogramma dat het hele programma in de programmeertaal omzet in een programma in machinetaal. Een interpreter is een programma dat de opdrachten in de programmeertaal rechtstreeks kan uitvoeren, zonder eerst het hele programma te hoeven compileren. Sommige talen hebben alleen een compiler, andere alleen een interpreter, weer andere beiden. Tot slot is het handig om een debugger te hebben: een programma dat meehelpt met het opsporen van fouten. Wanneer een text editor, compiler of interpreter en debugger in een programma samengevoegd zijn, spreekt men van een IDE, een integrated development environment. Elke IDE is in principe altijd geschikt voor slechts één programmeertaal. De eerste taal met een IDE was Dartmouth Basic uit 1964. De IDE werd aangesproken door het invoeren van commando’s, en niet met menu’s, zoals tegenwoordig. De eerste echt populaire IDE was Turbo Pascal. Ook Delphi, de opvolger ervan, werd veel gebruikt. De IDE’s zijn in de loop der jaren steeds geavanceerder geworden. Momenteel is Microsoft Visual Studio de populairste IDE. Deze bestaat uit IDE’s voor onder andere C++, Visual Basic en VB.NET. Een andere populaire IDE van Microsoft is Microsoft Development Environment, voor onder andere Java. Nieuw in de IDE van Visual Basic is dat niet langer alles via programmacode geprogrammeerd hoeft te worden, maar dat met de muis objecten, bijvoorbeeld elementen van een formulier, in het scherm gesleept kunnen worden.
Conclusie IDE’s worden de laatste jaren steeds uitgebreider. Op het moment zijn de geavanceerdste IDE’s die van Microsoft, bijvoorbeeld Visual Studio.
Figuur 2: Microsoft Visual Studio
Figuur 3: Microsoft Development Environment
24
25
Toepassingsgebied Natuurlijk zijn niet alle programma’s even geschikt voor alle toepassingen. Zo was efficiënt omgaan met rekenkracht in het begin van het computertijdperk erg belangrijk voor wetenschappelijke berekeningen. Voorbeelden van programma’s die daarop inspeelden waren Fortran en Algol. Andere talen moesten weer makkelijk te leren zijn (Basic), educatief verantwoord (om leerlingen goede programmeergewoonten aan te leren, Pascal), of geschikt voor zakelijk gebruik (Cobol). Sommige talen hebben heel specifieke toepassingsgebieden, zoals Lisp op het terrein van onderzoek naar kunstmatige intelligentie. Voor sommige toepassingsgebieden is een bepaalde taal het meest geschikt. Zo worden besturingssystemen meestal in C (en C++) geprogrammeerd, en simulaties vaak in C++. Visual Basic is het meest geschikt voor toepassingen voor Microsoft Windows. De laatste jaren worden talen steeds algemener, dat wil zeggen minder gericht op één doel. Toch zijn er nog steeds talen die hoofdzakelijk voor een doel worden gebruikt, bijvoorbeeld PHP en Perl voor het ontwerpen van internetpagina’s.
Conclusie De meeste programmeertalen zijn de laatste jaren zo uitgebreid geworden, dat ze voor alle toepassingen geschikt zijn. Daarom maakt het bij het kiezen van de programmeertaal niet zo veel meer uit, welk doel de programmeur wil bereiken.
26
Historie Hoe het begon De eerste computer was de Difference engine ontworpen door Charles Babbage in 1822. Het bleef bij het ontwerp, want de techniek was in Babbage’s tijd niet ver genoeg om deze machine ook werkelijk te bouwen. De manier moest ‘geprogrammeerd’ worden door de tandwielen die de berekeningen uitvoerden te vervangen of verplaatsen. De machine werd dus door fysieke beweging aangestuurd. Dit veranderde toen de Amerikaanse overheid in 1942 de ENIAC bouwde. Dit was een apparaat dat werkte met elektrische signalen. De principes van Babbage bleven voor het grootste deel wel bewaard. Zo kon het “programma” van dit gevaarte alleen worden veranderd door het omzetten van schakelaars en het anders aanleggen van de bedrading. In 1945 bedacht John Von Neumann twee ideeën die beide een grote invloed op de geschiedenis van programmeertalen hadden. Het eerste was dat het onhandig is de apparatuur elke keer fysiek aan te passen, dus hij vond dat de hardware zo simpel mogelijk moest worden gehouden. Deze simpele hardware moest worden bestuurd met behulp van ingewikkeldere instructies, waardoor de computer veel simpeler te herprogrammeren was. Het tweede idee kwam neer op het maken van subroutines, kleine blokjes code, die op elk gewenst moment aangeroepen konden worden, in plaats van vooraf de hele chronologie van het programma op te hoeven schrijven.
De eerste talen In 1949 ontstond de eerste echte programmeertaal: Short Code. Wel moesten hier de opdrachten nog steeds met de hand in nullen en enen omgezet worden. Dit veranderde met de uitvinding van de eerste compiler door Grace Hooper in 1951. Een compiler zet de opdrachten van de programmeertaal zelf om in machinetaal (nullen en enen). Nu hoefde de programmeur dit niet meer te doen, wat leidde tot het veel sneller kunnen programmeren. In 1957 ontstond de eerste van de echt grote programmeertalen: Fortran, een afkorting van Formula Translator. Deze taal was ontworpen door IBM voor het maken van wetenschappelijke berekeningen. De taal was erg primitief, en bevatte slechts opdrachten als GOTO, IF en DO. Al wel aanwezig waren de meeste datatypen van variabelen die we nu kennen, en logische waarden (waar en onwaar). Fortran was sterk op het gebied van berekeningen, maar minder goed op het terrein van invoer en uitvoer, punten die voor zakelijk gebruik erg belangrijk waren. In 1959 haakte de taal 27
Fortran op deze tekortkoming in. De taal was speciaal ontworpen voor zakelijk gebruik: invoer en uitvoer werkte beter, en de taal was redelijk simpel te leren. In 1958 werd LISt Processing, of kortweg LISP, ontworpen door John McCarthy. Het was speciaal ontworpen voor studie over kunstmatige intelligentie. LISP was een buitenbeentje onder de programmeertalen, wegens de vreemde syntaxis. Het was de eerste declaratieve programmeertaal. LISP is de oudste programmeertaal die tegenwoordig nog serieus gebruikt wordt. In 1958 werd Fortran opgevolgd door Algol als programmeertaal voor wetenschappelijk gebruik. Algol zelf was niet echt succesvol, zeker niet na 1968 toen er een nieuwere, maar veel ingewikkelder te gebruiken versie op de markt kwam. Wel zijn veel modernere programmeertalen zoals Pascal, C en JAVA op het concept van Algol gebaseerd. In 1964 werd door John Kennemy en Thomas Kurtz de taal BASIC ontwikkeld. Deze taal was ontwikkeld voor wetenschappers die geen ervaring met computers hadden, en was dus erg eenvoudig.
De ‘moderne’ talen Pascal was in 1968 ontworpen door Niklaus Wirth, vooral om zijn leerlingen een taal te geven waarmee ze op een goede manier leerden programmeren. Een van de manieren waarop Wirth de taal geschikt maakte voor het onderwijs was het toevoegen van een debugger, een manier om fouten in het programma op te sporen. Pascal was een combinatie van de beste elementen van Cobol, Fortran en Algol. De inconsequenties van deze talen verwenen in Pascal. Dit maakte Pascal, in tegenstelling tot Wirth’ bedoeling, erg populair. Later maakte Wirth nog een opvolger van Pascal, Modula-2, maar deze werd niet zo succesvol. In de tijd van Modula-2 raakte de taal C wel erg populair. C was ontworpen in 1972 door Dennis Ritchie. Het leek erg op Pascal, alleen zonder de fouten ervan. C was een erg krachtige taal, maar moeilijk te lezen. C was oorspronkelijk ontworpen voor het nieuwe Unix-systeem. Daarom gingen C en Unix vaak hand in hand. In C werden ook de meeste besturingssystemen geschreven, zoals Unix, Windows, MacOS en Linux. Eind jaren ’70 kwam Object georiënteerd programmeren, OOP, op. Bjarne Stroustroup gebruikte deze theorie om ‘C with Classes’ te schrijven, wat later leidde tot C++. C++ was bedoeld om de brute kracht van C te organiseren met behulp van OOP, zonder de snelheid daarbij te verliezen.
28
De jaren ‘90 In het begin van de jaren ’90 beschouwde men interactieve tv als de techniek van de toekomst. Sun Microsystems besloot dat deze taal wel een aparte programmeertaal kon gebruiken. Deze taal werd Java. Toen in 1994 echter bleek dat interactieve tv zou floppen, veranderden ze hun doelstelling in het aanpassen van hun taal voor het internet, de nieuwste mode. Het jaar daarna verleende Netscape een licentie aan SUN om de taal in hun browser te mogen toepassen. Java is helemaal gebaseerd op de ideeën van OOP, maar is wel een erg langzame taal. Microsoft was inmiddels ook in opkomst geraakt, en breidde Basic uit naar Visual Basic (VB). Het hart van Visual Basic is een formulier, waar je objecten als menuelementen en afbeeldingen op kan plaatsen. Al deze objecten kennen eigenschappen, zoals achtergrondkleur en tekst, en gebeurtenissen, zoals klikken en dubbelklikken. De meeste moderne interfaces werken op deze manier. Perl, ontworpen door Larry Wall in 1987, wordt vooral gebruikt voor scripts op internet. Deze taal was ontworpen omdat de functies van Unix om tekst te manipuleren niet langer aan Wall’s wensen voldeden. Inmiddels wordt Perl steeds meer vervangen door de opvolger ervan, PHP.
Conclusie De programmeertalen hebben zich de laatste jaren in een hoog tempo ontwikkeld. De vernieuwingen gaan zo snel, dat oudere talen al snel veel belangrijke nieuwe functies en concepten missen. De conclusie is simpel: de beste talen zijn de nieuwste…
29
Conclusie In dit werkstuk heb ik proberen te onderzoeken welke programmeertaal de beste is. Ik zal nu proberen een conclusie uit dat onderzoek te trekken. Op het eerste gezicht ziet elke programmeertaal er totaal anders uit, maar nader beschouwd lijken in elk geval de talen binnen een paradigma erg op elkaar. Onderling verschillen de paradigma’s wel sterk. Door de grote verschillen is het niet mogelijk een paradigma als ‘het beste’ aan te wijzen. De nieuwe talen zijn over het algemeen de beste, ongeacht het doel dat de programmeur wil bereiken. De beste IDE is op dit moment waarschijnlijk Microsoft Visual Studio.
30
Bronvermelding Bij het maken van dit werkstuk zijn de volgende bronnen gebruikt: Jeroen Fokker en Willem van der Vegt, Programmeertalen en paradigma’s Pixel, Programming language study http://merd.net/pixel/language-study/ Doug, The great computer language shooutout http://www.bagley.org/~doug/shootout/ Andrew Ferguson, The history of computer programming languages http://www.princeton.edu/~ferguson/adw/programming_languages.shtml The Brighton University Resource Kit for Students, Adventure in Prolog http://burks.bton.ac.uk/burks/language/prolog/amzi/ Collin Alen, Lisp Primer http://grimpeur.tamu.edu/~colin/lp/ Wikipedia Lemma’s “Hello_world_program” en “Integrated_development_environment” http://en.wikipedia.org/
31