|
Post by Marco Kurvers on Aug 2, 2024 17:00:50 GMT
In Q(uick)BASIC en in QB64, zie de Board, wordt al veel geprogrammeerd met subroutines en functies.
Carl Gundel heeft deze mogelijkheid ook toegevoegd in Liberty BASIC. Zie de volgende post hierover.
|
|
|
Post by willembever on Aug 2, 2024 17:32:43 GMT
Carl Gundel heeft het een en ander over dit onderwerp in zijn boek geschreven.
In 2009 publiceerde Carl het boek “Beginning Programming with Liberty BASIC.” Op dat moment was LB v4.03 de nieuwste versie. Carl schrijft dan ; “This is the version of Basic we are going to teach you in this cours.”(blz.9) De inleiding van hoofdstuk 5 is interessant: When programs begin to grow beyond a page or so of code they can begin to become hard to work with. Without some care and good practices, the meaning of a computer program can become lost. This isn't about the meaning according to the computer. The computer will never know (or care) what the program means, but if the computer can execute the code that will be enough for the computer. What does the program mean when a person reads the code? That's what I meant! When people build something complicated, they build it out of parts. We don't say about a car, "This piece of metal is attached to this other piece of metal with another piece of metal," do we? No, of course not! We say, “This water pump is bolted onto the engine block!" Each part does something special and each part has an equally special name. We will learn to build software this way, as a system made out of parts named for the special part they play. (blz. 63) Hierna begint Carl met een paragraaf Modularity. Op bladzijde 69 begint de paragraaf Call en Sub: Sometimes it can be hard to write reusable code with GOSUB/RETURN. The biggest problem is that each section of code in your program that wants to use a subroutine may have different variable names for the things you want your subroutine to work on. This makes for a little extra work. Carl plaatst dan een voorbeeld programma met Gosub/Return en laat daarna zien hoe dit met Sub/End Sub kan worden geschreven. Carl zegt dan op bladzijde 71: We replace the branch label and RETURN command from the first example with SUB and END SUB commands. We can execute this subroutine with a CALL command. This creates another kind of subroutine which works similarly to the GOSUB/RETURN type we looked at earlier, but it has a very special feature. This kind of subroutine has its own variables, which cannot be seen by program code outside of the subroutine. It also works the other way. The code inside the subroutine cannot see variables outside. This is a common feature of many programming languages, called scope. If a variable is visible in one place, this is called local scope and if a variable is visible everywhere this is called global scope. En op bladzijde 72 zegt Carl dan: One really useful feature of scoping is that you can use the same variable names in different subroutines and they will not conflict. A variable named count in the program's main code could hold the number of survey questions answered, and another variable named count in another subroutine might be used to count the number of times the answer was Y. Here is a really short program that shows how two variables with the same name in two different places each have their own values independent of each other. Carl laat dan weer een paar voorbeelden zien en zegt dan op bladzijde 73: You only need to run this example to see the value in being able to use the same variable name in different places. The FOR/NEXT loop should seem to loop 5 times, counting to 5, but it doesn't. It's such a simple thing, and it doesn't work! The reason isn't revealed until you examine the subroutine. The bigger your program becomes, the more valuable local scoping becomes. The more code you write, the more you will want to reuse code between programs. This is a much easier matter if you don't have to worry about conflicting variable names. With local scoping, just copy and paste the subroutine between programs, and you're good. Wat betreft Function/End Function schrijft Carl op bladzijde 74: This is very similar to the prior example that uses SUB/END SUB but instead it uses FUNCTION/END FUNCTION. A user function is also a kind of subroutine, but it retums a value. You use it just like you use Liberty BASIC's built-in functions like STR$( ), WORD$( ), etc. What can you do with the returned value? - print it out - assign it to a variable - use it as part of a larger expression We gaan dan door naar hoofdstuk 10 waar op bladzijde 131 is te lezen: So far we have showed how to specify handlers using branch labels such as [okClicked] and [quit]. When we covered the use of the SUB statement back in chapter 5 we also explained that using SUBs and FUNCTIONs is preferable to code using branch labels because they have stronger scoping. This makes it easier to keep the different parts of your program untangled. We can take advantage of the benefits of SUBs when we use controls. Instead of specifying the name of a branch label when we add a control we specify the name of a SUB. Wat staat hier? “We also explained that using SUBs and FUNCTIONs is preferable to code using branch labels” Of wel in het Nederlands: “We verklaarden dat het gebruik van Sub’s en Function’s beter is dan code met gebruik van branch labels.” Wel, alles bij elkaar heeft Carl Gundel in zijn boek het dus over de voordelen van het gebruik van Sub en Function en het voordeel van de inkapseling van variabelen. Voor wie geïnteresseerd is in het boek: ISBN 978-0-557-22811-9
|
|
|
Post by frizhd on Aug 20, 2024 15:40:53 GMT
Het ‘overdragen’ van de inhoud (een getal of tekststring) van variabelen aan een subroutine, een subprogramma, een machinetaalroutine etc. is niet alleen een kernprobleem in Liberty Basic. Dit ‘doorgeven’ is een fundamentele problematiek van iedere programmeertaal. De noodzaak daarvan zagen de aartsvaders van BASIC, Kemeny en Kurtz van BASIC ook heus in. Hun TRUEBASIC uit 1987 kent twee commando’s: De GOSUB/RETURN en SUB /END SUB. De eerst werkt GLOBAL en de tweede LOCAL. Wat dat is, snapt iedere ‘professional’, maar de ‘lekenbroeder’, ‘hobbyist’, ‘beginner’ niet. Getuige zijn de talrijke discussies erover op allerlei fora van diverse Basisdialecten. Het lukt de NERDS maar niet om die DUMMIES dat uit te leggen. Die discussies hebben een vreselijk lange BAARD gekregen en leiden tot onbegrip. De hamvraag echter is. Wanneer moet, of mag je welke gebruiken? Zie bijvoorbeeld: qb64phoenix.com/forum/showthread.php?tid=2920&highlight=gosubVoor de beroeps is het evident. Hij heeft zich te voegen aan het regiem van zijn werkgever en doet daarom minachtend over dat GLOBALE gedoe en noemt het soms zelfs ROMMEL. Weg met GOTO, GOSUB, LABELS. Spaghetti, ongestructureerd, onleesbaar zijn enkele epitheta. Alles in SUBS en FUNCTIONS. Dat is pas modern, een technische verbetering! En men doet daarom BASIC als taal voor de beginner in de ban. Hier valt niet professioneel mee te werken en is als leermiddel ongeschikt om aan je kinderen door te geven. Een poosje nog en het is een dode taal, sterft vanzelf uit. Gundel gaat in de eerste 69 blazijden uit van de GOSUB en wenst de lezer in een katerntje ‘Many Happy Returns’. Dan gaat hij over op de tekortkomingen ervan en de betekenis van de CALL SUB. In zijn BLOG fulmineert hij tegen zeloten die het liefst over zijn Liberty Basic de ban willen uitspreken en PYTHON als de nieuwe kleren van de keizer aanprijzen. microcomputing.blogspot.com/2018/09/python-emperors-new-clothes.htmlDe slotvraag Zijn er met dat gemoderniseerde BASIC inderdaad lange en mooie programma’s te maken? Of trekken we aan een dood paard?
|
|
|
Post by Marco Kurvers on Aug 21, 2024 17:18:26 GMT
Ja friz, helaas denken de beroepsmensen inderdaad zo. GOSUB/RETURN, labels en GOTO. Dat is spaghetti en kunnen we daarom niet in ons bedrijfsleven gebruiken. Maar wij als hobbyisten denken daar natuurlijk heel anders over. Globaal programmeren, is dat tijdrovend en veroorzaakt dat spaghetti? Alleen mensen die er niet goed weten mee om te gaan, kunnen de spaghetti veroorzaken. Maar mensen als wij weten er zeker wel mee om te gaan. Of het nou tijdrovend is of niet, programma's kunnen er nog steeds mee geschreven worden.
Je kunt in een oude pak blijven lopen, maar daar in het bedrijfsleven willen ze liever de moderne technieken zien. Ik gebruik het ook, zoals je het van mij weet, maar dat betekent niet dat ik geen oude pak meer aan zou willen trekken.
In ieder geval: BASIC zal nooit en te nimmer afsterven.
|
|
|
Post by willembever on Aug 21, 2024 19:51:54 GMT
Tja, nu moet ik toch van het onderwerp afwijken om de vraag van Friz te beantwoorden. Mogelijk trekken we aan een stervend paard. BASIC werd indertijd bedoeld als een taal om mee te leren wat een computer programma doet. Om daarna op een andere taal, een die toen professioneel werd gebruikt, over te stappen. Maar ja, toen vonden een stel hobbyisten de home computer uit. 8 Bits processor, 8 tot 16 kbyte RAM. Daar paste precies die simpele BASIC in. Weet je nog, programma's opnemen op een compact cassette. En zo werd een hele generatie opgevoed met BASIC. Alleen, in vreselijk veel dialecten. Maar ja, wie gebruikt er heden ten dage nog een Sony walkman?
Maar hoe gaat het nu? In Leiden kennen ze een zgn. CoderDojo. Ik heb daar in de bibliotheek wel eens naar staan kijken. Op de homepagina zeggen ze dat zo: Welkom bij CoderDojo Leiden Tijdens onze (gratis) dojo’s leren kinderen van 7 tot 17 jaar om programmeertalen te begrijpen, oftewel “programmatisch te denken”, wat van toenemend belang is in de moderne wereld. Samen met andere deelnemers leren de kinderen vaardigheden zoals het bouwen van een website, het aansturen van een robot of het maken van een applicatie of game. Dit alles twee keer per maand georganiseerd en ondersteund door een enthousiaste groep van vrijwilligers. Als ik dan eens rond kijk dan zie ik de volgende items: Scratch, Python, JavaScript en webpagina's maken met HTML & CSS. En daar is dus geen woord BASIC bij. Wat ik bij CoderDojo ook zie is ook Games ontwerpen. Daarbij wordt het programma Unity genoemd waar Marco het bij games ontwerpen ook over heeft. En ook Unity gebruikt een moderne programmeertaal om de games te ontwerpen. Als ik daar bij die CoderDojo eens mee stond te kijken naar de schermen, dan zag ik die beginnende DUMMIES van 7 tot 17 jaar vooral bezig in Python en JavaScript. Zijn die talen dan minder moeilijk dan BASIC? Kortom, er is een kans dat, als de generatie die op 8 bits homecomputers is begonnen in BASIC uitsterft, dat BASIC dat ook doet.
Maar tot dat moment, laat een ieder BASIC gebruiken op de manier waarop hij/zij dat het prettigst vindt. De een gebruikt liefst Gosub/Return, de ander Sub/End Sub. Elk heeft zo zijn voordelen. Belangrijk blijft netjes werken. Want zelfs met Sub kun je nog spaghetti code schrijven. Blijft over Tessalonicenzen 5:21-22. Maar vergeet niet, wat voor de een het goede is, is niet altijd het goede voor een ander. Dus laat een ieder zijn eigen manier bepalen, of dat nou jaren '70 (Gosub) stijl is of de stijl vanaf jaren '90 (QBASIC met Sub en Function) is.
Het belangrijkste is om plezier te hebben in programmeren.
|
|
|
Post by Marco Kurvers on Aug 21, 2024 20:20:59 GMT
Onderstaand programma laat een mogelijkheid zien dat we variabelen kunnen gebruiken in een subroutine, zonder deze GLOBAL te noemen. Het gebruik van een label event is de uitkomst. Op die manier kunnen we de subroutine in de label aanroepen met de variabele als argument.
Zie ook een mooie programmeermanier om meerdere paragrafen te tonen in een staticbox zonder steeds met de tekst te herhalen door gebruik van een Resultaat$() functie. Bekijk eens die functie en zie wat de EVAL$() functie doet. Dit vervangt de mogelijkheid om te moeten controleren wat er berekent moet worden.
constString$ = "De twee waarden zijn " WindowWidth = 800 WindowHeight = 600 statictext #w.st1, "Waarde 1", 50, 50, 200, 45 textbox #w.txtWaarde1, 50, 100, 100, 45 statictext #w.st2, "Waarde 1", 50, 200, 200, 45 textbox #w.txtWaarde2, 50, 250, 100, 45 stylebits #w.stResult, _WS_BORDER, 0, 0, 0 statictext #w.stResult, "", 300, 50, 300, 300 button #w.btnKlikMij, "Klik mij", [btnKlikMij], UL, WindowWidth - 200, WindowHeight - 150, 150, 45 open "Sub events en variabelen" for window as #w #w "trapclose Quit" #w "font arial 12" wait
sub Quit handle$ close #handle$ end end sub
[btnKlikMij] call btnKlikMij constString$ wait
sub btnKlikMij string$ #w.txtWaarde1 "!contents? waarde1" #w.txtWaarde2 "!contents? waarde2" '#w.stResult "De twee waarden zijn vermenigvuldigd:"; chr$(13); waarde1; " * "; waarde2; " = "; waarde1 * waarde2; _ ' chr$(13); chr$(13); "De twee waarden zijn gedeeld:"; chr$(13); waarde1; " / "; waarde2; " = "; waarde1 / waarde2 #w.stResult string$; Resultaat$("*vermenigvuldigd", waarde1, waarde2); chr$(13); chr$(13); _ string$; Resultaat$("/gedeeld", waarde1, waarde2); chr$(13); chr$(13); _ string$; Resultaat$("+opgeteld", waarde1, waarde2) end sub
function Resultaat$(operator$, waarde1, waarde2) symb$ = left$(operator$, 1) operatorStr$ = mid$(operator$, 2) Resultaat$ = operatorStr$; ":"; chr$(13); waarde1; " "; symb$; " "; waarde2; " = "; _ eval$(waarde1; symb$; waarde2) end function
|
|
|
Post by frizhd on Aug 22, 2024 17:31:57 GMT
Zo is het maar net Marco! Wie met AapNootMies heeft leren lezen, zal Worteltrekken nooit onder de knie krijgen. Python al helemaal niet, want reeds te oud zonder akte Engels, dat ligt niet meer in mijn bereik. Met LEGO mocht ik ook niet spelen en stripverhalen verknoeiden eens en voorgoed mijn leesvaardigheid. Hopeloos geval. De technische vooruitgang achtervolgt me nu eenmaal. Als ik te horen krijg van de redactie, dat mijn SUB niet geplaatst, cq verwijderd is, omdat het in minusculi (Ital) is geschreven en ik me moet verdiepen aan het JuisteGebruik van HOOFDLETTERS … (Geen censuur hoor!!) Nou ja, terwijl wetenschappelijk onderzoek toch heeft bewezen dat de leesbaarheid van kleine lettertjes veel beter is, codeer ik argeloos verder….
GOSUB [AmmeHoela] goto CALL scheit
Broeder Bever, dank voor de bemoediging. Mijn garagist vond gelukkig een oude VARIOMATIC van een DAFje en heeft die op mijn scootmobiel gezet. Daarmee kunt U best Uw rijbewijs nog halen, lekker scheuren en lol beleven! Een FIAT 500 heeft immers ook geen uitlaat van een Masserati nodig. Laat die jongens hun enige venstertje toch gewoon met een subroutine QUITen.
|
|
|
Post by memophenon on Aug 23, 2024 14:01:48 GMT
Wanneer je een professional tegenkomt die beweert dat je in zoiets als GW-BASIC niet gestructureerd kunt programmeren, heb je niet met een professional te maken. Wel met iemand die zichzelf nogal hoog heeft zitten. Nu is de term 'gestructureerd programmeren' een vlag waarmee vele ladingen kunnen worden gedekt. Ik versta er iets heel specifieks onder, en onderscheid in elk geval een ander begrip: modulair programmeren. Dat is echt iets anders. Je kunt ongestructureerd modulair programmeren en gestructureerd programmeren zonder modules (op één na dan, wat het laatste betreft — leg ik zo dadelijk uit). Het mooiste is het om beide technieken te combineren. Ze lijken haast voor elkaar gemaakt. Ik werk hier alleen het begrip modulariteit uit, want dat is waar het bij subroutines en functies om gaat.
Een in een procedurele taal geschreven programma bevat statements. Een reeks aaneengesloten statements noemen we een segment. Een identificeerbaar segment waarvan het begin en het eind duidelijk afgebakend is, noemen we een module. 'Identificeerbaar' houdt in dat het ding een naam (of desnoods een nummer) heeft, zodat je het kunt aanroepen. Alles wat ik in deze alinea te berde breng, zijn lexicografische begrippen. Dat wil zeggen, wat je leest als je van boven naar beneden de de broncode doorneemt. Het heeft dus niet direct betrekking op het proces dat loopt wanneer je het programma uitvoert, al heeft wat je opschrijft natuurlijk wel gevolgen voor wat er tijdens die uitvoering zoal gebeurt.
Het programma zelf is dus een module: het heeft een naam, een begin en een einde. Modulair programmeren houdt in, dat je binnen het programma andere modules gebruikt. We zouden modules zoals ik hierboven heb gedefinieerd kunnen kwalificeren als niveau M0. Zelf zou ik de lat nog een tikkeltje hoger willen leggen, op niveau M1: je kunt niet zomaar een module binnenrollen, je moet hem expliciet aanroepen. Nog een niveau hoger is M2: de module bevat eigen variabelen die buiten die module onzichtbaar zijn. Lokale variabelen, inderdaad. Het kan nog sterker op niveau M3: de waarde van lokale variabelen kan over de verschillende activeringen van de module bewaard blijven. Men spreekt dan van statische variabelen. Sommigen propageren het totaal vermijden van globale variabelen binnen een module. Laten we dat als niveaus M2+ resp. M3+ aanduiden. Ik wil daar nu niet over uitwijden.
Je ziet dat modules in verschillende graden van zelfstandigheid kunnen voorkomen. Die zelfstandigheid, dat is precies waar het om gaat. Dat een module zo min mogelijk afhankelijk is van zijn omgeving, en dat de omgeving geen last heeft van de interne aangelegenheden van de module. Een kwestie van hygiëne, zogezegd.
Goed, hoe is het dan met BASIC gesteld? Laten we bij het begin beginnen: de eerste Dartmouth BASIC versie van 1 mei 1964. De GOSUB-constructie is geen module. De afbakening is daar te vaag voor. Je zou het op z'n welwillendst kunnen betitelen als een schiermodule, vanwege het RETURN-mechanisme. Maar er is wel degelijk een echte, zij het wat primitieve modulevorm in de taal aanwezig, namelijk de functiedefinitie d.m.v. DEF FN:
DEF FNA(N) = N * A Hierin is A een globale variabele, en N een lokale. We zitten dus meteen al op niveau M2. Met helaas wel met een heel vervelende beperking: de inhoud van zo'n functie kan niet meer dan één expressie zijn. Ach, het was ook maar een beginnetje. Hoe de tweede en derde versies van Dartmouth BASIC er precies uitzagen, is moeilijk te achterhalen. Maar in de vierde versie, die in januari 1968 uitkwam, was genoemde beperking geheel verdwenen, al zat er nog een wat ruw kantje aan:
100 LET A = 42 200 LET B = 21 300 DEF FNA(N, B) 310 LET B = 1 320 PRINT A, B, N 330 LET FNA = N * (A + B) 399 FNEND 800 PRINT A, B, FNA(2, 0), B 999 END Dit is de uitvoer ervan:
42 21 42 1 2 86 21 Voor wie Dartmouth BASIC Fourth Edition niet bij de hand heeft: het werkt ook in QBasic, QuickBASIC en True BASIC. Je moet dan wel regel 399 vervangen door:
399 END DEF (QB64 PE doet niet mee, want die wil het DEF FN-statement niet kennen.) In latere Basic-versies en -dialecten is een en ander nog een beetje bijgeschaafd. De essentie van het M2-model was echter van begin af aan aanwezig.
Lange tijd zijn er geen wezenlijke uitbreidingen van programmeertaalconcepten bij gekomen. De ontwikkelingen staan weliswaar niet helemaal stil, maar de Basic-wereld hobbelt daarin op flinke afstand achter andere talen aan. Niet zo interessant allemaal. Die achterstand is op zich niet per se een tekortkoming. Ik zou zelfs liever zien dat Basic op een zeker moment gewoon af was. Ergens op een punt heel ver vóór VB.NET. Ik waardeer het oudere Basic juist als een eenvoudige en betrouwbare M3-taal, waarin ik aardig mijn bedoelingen kan uitdrukken als ik er om een of andere reden op aangewezen ben.
Het staat wat mij betreft iedereen vrij om roomser te zijn dan de paus, psalmen in hele noten te zingen of modulariteit af te wijzen. Als dat is omdat men niet anders kan, het zij zo. Maar een beetje jammer is het wel.
|
|
|
Post by Marco Kurvers on Aug 25, 2024 8:14:57 GMT
Een mooie uitleg memophenon. Ja, BASIC hobbelt erachter aan, maar wij als BASIC gebruikers zouden dat nu niet meer erg hoeven te vinden. Liberty BASIC heeft de mogelijkheden van Q(uick)BASIC, hetzij wel op een beperkte manier. De DEF FN met een END DEF heb ik vaker gezien. Het lijkt op een DEF FN<funcnaam>(<params>) ... = expressie in BBC BASIC. BBC BASIC kent geen END DEF, maar wel om met een =-teken als een RETURN af te sluiten. Overigens, Q(uick)BASIC gebruikt maar een één-regel DEF FN. Niet met een blok en dan afsluiten met een END DEF. Als je dit in Q(uick)BASIC wilt doen, dan heb je een FUNCTION <funcnaam>(<params> ... <funcnaam> = <expr> END FUNCTION nodig. Overigens, een programma kun je inderdaad een module noemen. Maar een code in een SUB ... END SUB | FUNCTION ... END FUNCTION heet een codeblok, maar geen module. Modules bestaan alleen uit subroutines en functies zonder een hoofdprogramma. Je hebt ook templates. Dat zijn programma's waar geen inhoud in zit, alleen geraamte. Zoals een venster die je alvast klaarmaakt om in meer programma's te gebruiken. Er is geen inhoud, alleen een subroutine die al klaar is gemaakt om het venster te sluiten. In Liberty BASIC kunnen we modulair programmeren door andere programma's in een hoofdprogramma in te voegen. In Liberty BASIC 5 werkt een module als een library, met dezelfde naam als commando.
Dankzij Liberty BASIC zal BASIC niet verdwijnen. Ook al is de BASIC taal beperkt, we kunnen er veel mee. Met ook nog eens een makkelijke manier om vensters te maken en de GUI te gebruiken. Ongestructureerd programmeren, zoals vroeger, kan nog steeds. Liberty BASIC heeft een compatibiliteit om zeer oude BASIC listings te kunnen starten. Maar je kunt in Liberty BASIC met labels, GOSUB ... RETURN en GOTO ook gestructureerd programmeren. Friz is daar heel handig in.
Omdat ik ook vele andere programmeertalen gebruik, heb ik er voor gekozen om de subroutines en functies te gebruiken in Liberty BASIC. Echter, ik gebruik de labels vaak erbij omdat labels best wel eens handig kunnen zijn, zoals je in bovenstaand programma ziet.
Je kunt modulair programmeren met subroutines en functies. Er is een techniek dat encapsulation heet, waarmee je lokale code kunt uitvoeren dat niet een ander hoeft te weten. Het enige wat een ander moet doen is het aanroepen van de subroutines en functies en de waarden meegeven. Helaas is encapsulation niet te gebruiken in een globaal programma, omdat de ander dan wel juist moet weten wat voor inhoud er is vanwege dat variabelen de waarden moeten hebben die je mee wilt geven. Omdat labels geen parameters hebben, moet men dus de variabelen "kennen" om de waarden te geven. Een ander probleem is dat een GOSUB aanroep niet in een expressie werkt. Een expressie moet dan in delen worden geschreven om de GOSUB commando's aan te roepen. Is dat ongestructureerd programmeren? Mijn antwoord is nee. Je programmeert pas ongestructureerd als je door de bomen het bos niet meer ziet, omdat dan de structuur door elkaar ligt. Als je goed weet wat je doet, dan programmeer je gestructureerd - of het nou labels zijn of subroutines en functies, dat maakt niet uit.
|
|
|
Post by memophenon on Aug 25, 2024 21:17:11 GMT
Hopelijk heb je, Marco Kurvers, in mijn opmerking over dat hobbelen verstaan dat ik vind dat het hobbelen maar eens moet stoppen. Niet nòg meer concepten in de taal proppen. Mijn idee hierover is eigenlijk veel radicaler, maar dat bewaar ik voor een andere keer en een andere plaats. Misschien moet er ergens in dit forum een hoekje Mopperen over Basic of zoiets worden aangelegd. Of is dat er al?
Met deze post wil ik ingaan op jouw en mijn taalgebruik. Jij hebt je termen helemaal toegespitst op Liberty BASIC. Niet onterecht, want daar gaat deze thread tenslotte over. Ik heb gepoogd de grotere (taal- en dialectonafhankelijke) lijnen uit te zetten en daar meer algemene termen voor te bezigen. Dat kan conflicteren met specifieke benamingen. Met name bij de term 'module' heb ik geaarzeld en wel even 'compartiment' of iets van dien aard overwogen. Maar ja, het ingeburgerde begrip luidt nu eenmaal 'modulair programmeren' (zo heet ook het lemma in wikipedia), daarom heb ik het toch maar bij 'module' gelaten. Wat jij 'module' en 'codeblok' hebt genoemd, valt dus allemaal onder mijn paraplubegrip 'module'. Ook fenomenen als macro's en coroutines (kent LB zulke mechanismen?) zijn als module op te vatten, mits ze voldoen aan de kenmerken van M0: het ding heeft een naam, een begin en een eind.
Aan de drie eisen om als M0 te mogen worden gekwalificeerd voeg ik hier een vierde toe: indien twee modules elkaar overlappen, ligt de ene geheel binnen de andere en vallen ze niet geheel samen. Dat ruimtelijke aspect had ik de vorige keer over het hoofd gezien.
Ik heb nog even naar je code met dat label gekeken. Dat een button in staat is naar een label te springen, lijkt me niet per se laakbaar. Ik vermoed dat het dan de 'GOWAIT'-variant (mijn benaming) in de GO-familie betreft, die tot een vervolgsprong naar elders (waar?) leidt wanneer het keyword WAIT opduikt. Verbeter me als ik fout zit, dit is een hinkelspelletje dat ik niet ken. Dat het label en de Sub btnKlikMij dezelfde naam hebben, zette me trouwens aanvankelijk op het verkeerde been. Mogelijk is dit een conventie bij de inrichting van een GUI. Er valt inderdaad veel voor te zeggen om via zulke naamgevingen de aan elkaar gerelateerde elementen bij elkaar te houden.
Wat het bedreven programmeren van frizhd betreft: zelfs met alleen GOTOs kun je gestructureerd programmeren. Is wel weer een graadje lastiger. Voordat ik verder commentaar geeft op wat hij in deze thread heeft aangevoerd, wil ik eerst serieuze aandacht besteden aan de confrontatie van zijn gezichtspunten met de werkelijkheid van bestaande Basic-dialecten en de situatie waarin de Basic-programmeur verkeert. Ik verwacht na enig onderzoek en nadenken wel met wat tegenstrijdige doch houtsnijdende conclusies te kunnen komen.
En zo'n codeblok DEF FN<functionname> ... END DEF werkt echt hoor, in Q(uick)BASIC!
|
|
|
Post by willembever on Aug 27, 2024 9:19:48 GMT
Fritz, bedankt voor de verduidelijking. Ik snap nu hoe het mogelijk is dat mensen welke in een scootmobiel in een voetgangers gebied van een winkelcentrum rijden dat doen met een snelheid die een moderne fatbike niet zou misstaan: ze hebben er een variomatic in ingebouwd! Nog even en ze moeten verplicht een helm dragen.
|
|
|
Post by memophenon on Sept 1, 2024 9:11:57 GMT
Om mezelf nog wat tijd te geven enigszins thuis te raken in Liberty BASIC, behandel ik eerst maar de link van frizhd naar het qb64phoenix-forum. Er wordt daar veel over allerlei stijlaspecten gesproken. Programmeerstijl is inderdaad zo'n onderwerp waar je hele fora mee kunt vullen. Wat er specifiek over GOSUB en SUB te berde is gebracht, valt helaas in de categorie slap geleuter, op één pareltje na. Ik ga nu proberen dat beter te doen. Ik heb daartoe drie programmaatjes geschreven die steeds hetzelfde doen: van vijf woorden drie woordparen maken, zodanig dat elk woord maar in één paar voorkomt. Dit is de gemeenschappelijke uitvoer: <ALPHA,BRAVO> <CHARLIE,DELTA> <ECHO> Een beetje sneu voor ECHO, maar ik heb het er niet nog eens dieper willen inwrijven door er een komma achter te zetten. Voor het programmeerwerk heb ik me zolang maar even beholpen met True BASIC. Onderstaande listing is in de oude, niet-modulaire Basic-stijl geschreven. 100 REM ---- True BASIC program #1: GOTO and GOSUB ---- 110 DIM A$(3) 120 LET W1$ = "ALPHA" 130 LET W2$ = "BRAVO" 140 LET W3$ = "CHARLIE" 150 LET W4$ = "DELTA" 160 LET W5$ = "ECHO" 170 GOTO 300 200 IF LEN(L$) = 0 OR LEN(R$) = 0 THEN LET C$ = "" ELSE LET C$ = "," 210 LET P$ = "<" & L$ & C$ & R$ & ">" 299 RETURN 300 LET L$ = W1$ 310 LET R$ = W2$ 320 GOSUB 200 330 LET A$(1) = P$ 400 LET L$ = W3$ 410 LET R$ = W4$ 420 GOSUB 200 430 LET A$(2) = P$ 500 LET L$ = W5$ 510 LET R$ = W6$ 520 GOSUB 200 530 LET A$(3) = P$ 600 FOR I = 1 TO 3 610 PRINT A$(I) 699 NEXT I 999 END Dit heb ik vervolgens omgewerkt naar een nieuwe stijl, te weten een compleet modulaire opzet. Het codesegment 200–299 is hiertoe vervangen door de SUB CONCAT (een module van niveau M1) en de drie GOSUBs door CALLs: !---- True BASIC program #2: SUB and global variables only ----!
DIM A$(3) LET W1$ = "ALPHA" LET W2$ = "BRAVO" LET W3$ = "CHARLIE" LET W4$ = "DELTA" LET W5$ = "ECHO"
SUB CONCAT IF LEN(L$) = 0 OR LEN(R$) = 0 THEN LET C$ = "" ELSE LET C$ = "," LET P$ = "<" & L$ & C$ & R$ & ">" END SUB
LET L$ = W1$ LET R$ = W2$ CALL CONCAT LET A$(1) = P$
LET L$ = W3$ LET R$ = W4$ CALL CONCAT LET A$(2) = P$
LET L$ = W5$ LET R$ = W6$ CALL CONCAT LET A$(3) = P$
FOR I = 1 TO 3 PRINT A$(I) NEXT I
END Voordeel: het programma kan tijdens de uitvoering niet per ongeluk de CONCAT-routine binnenrollen. Het gedeelte SUB ... END SUB is alleen maar een definitie, en wordt pas uitgevoerd wanneer het expliciet wordt aangeroepen. Je hoeft er niet overheen te springen, sterker nog, je màg er niet overheen springen, want dan zou CONCAT ongedefinieerd blijven. Nadeel: deze constructie kost één regel meer, namelijk die beginregel SUB CONCAT. En je kunt er geen spaghetti mee bereiden. Maar dat wilden we immers helemaal niet, toch? De beschikbaarheid van SUBs maakt GOSUBs volkomen overbodig en opent bovendien deuren naar andere voorzieningen, die ik in het derde programma zal demonstreren. Tijd voor een eerste advies. Schrijf je programma in één stijl. Oude of nieuwe, maar geen ratjetoe van die twee door elkaar. Schrijf nieuwe programma's in nieuwe stijl (zonder GOSUB dus). Handhaaf bij onderhoud van programma's in oude stijl gerust die oude stijl (met GOSUB), of zet het programma anders volledig om naar de nieuwe stijl. Tot hier toe is het hopelijk ook voor hobbyisten wel te volgen. Gaan we ons thans wagen aan een in deze kringen gevreesd onderwerp: lokale variabelen. Bovenstaande listings bevatten slechts één soort variabelen: globale variabelen voor globaal gebruik en globale variabelen voor lokaal gebruik. A$, W1$, W2$, W3$, W4$, W5$ en W6$ zijn voor globaal gebruik, terwijl L$, R$, C$ en P$ voor lokaal gebruik zijn bedoeld, namelijk in segment 200–299 (oude stijl) of de subroutine CONCAT (nieuwe stijl). Vóór en na de GOSUB dan wel CALL worden er waarden van globale variabelen voor globaal gebruik overheveld naar globale variabelen voor lokaal gebruik, en vice versa. Door gebruik te maken van ècht lokale variabelen, kan dat overgooien verregaand worden gestroomlijnd. Tweede advies: zet alle (ALLE!) globale variabelen voor lokaal gebruik om naar lokale variabelen. Dit is met enkele eenvoudige handgrepen te verwerkelijken: !---- True BASIC program #3: SUB, global and local variables ----!
DIM A$(3) LET W1$ = "ALPHA" LET W2$ = "BRAVO" LET W3$ = "CHARLIE" LET W4$ = "DELTA" LET W5$ = "ECHO"
SUB CONCAT(L$, R$, P$) LOCAL C$ IF LEN(L$) = 0 OR LEN(R$) = 0 THEN LET C$ = "" ELSE LET C$ = "," LET P$ = "<" & L$ & C$ & R$ & ">" END SUB
CALL CONCAT(W1$, W2$, A$(1))
CALL CONCAT(W3$, W4$, A$(2))
CALL CONCAT(W5$, W6$, A$(3))
FOR I = 1 TO 3 PRINT A$(I) NEXT I
END CONCAT is hier een niveau M2+ module geworden. L$, R$ en P$ zijn lokale variabelen die bij de aanroep van CONCAT gegevens uitwisselen met de omgeving. Zulke variabelen, die tussen ronde haakjes na de naam van de SUB worden gedeclareerd, worden formele parameters genoemd. In dit geval fungeren L$ en R$ als invoerparameters en P$ als uitvoerparameter. De corresponderende parameters waarmee de SUB in een CALL-statement wordt aangeroepen (in dit geval toevallig allemaal globale variabelen) staan eveneens tussen haakjes en worden actuele parameters of ook wel argumenten genoemd. Het transport van waarden tussen de buitenwereld en de lokale variabelen van de subroutine gaat vervolgens vanzelf, dat hoeft allemaal niet meer stuk voor stuk via afzonderlijke toekenningen te worden uitgeschreven. We houden nog één lokale variabele over, C$. Die dient puur voor intern gebruik binnen de SUB. Het mooie van deze constructie is, dat je je bij de binnen CONCAT voorkomende variabelen niet hoeft af te vragen of deze elders in het programma misschien ook nog een rol spelen. Het antwoord is simpelweg: nee. Want je ziet ogenblikkelijk dat ze òf als parameter òf als LOCAL zijn gedeclareerd. Schrandere hobbyisten zullen mogelijk de vraag stellen, of globale variabelen voor globaal gebruik binnen een SUB mogen worden gelezen of zelfs gemuteerd (we zouden op die manier dus een stapje terug doen van niveau M2+ naar M2). Ik kan daar niet zo'n kort en helder antwoord op geven als ik hierboven heb gedaan. Muteren wordt vaak als onzindelijk gezien en lezen is niet geheel zonder gevaar, maar verboden is het niet. De vorige zin mag je wat mij betreft naar believen afserveren als vaagpraat. Maak je er voorlopig maar niet al te druk over. Volg de twee adviezen en je hebt het qua modularisering al heel mooi gedaan. Dat pareltje waar ik gewag van maakte. Dat wil ik nog wel even in het licht zetten. Voor zover ik Basic-dialecten ken, hebben ze allemaal de beperking dat je binnen een SUB- of FUNCTION-definitie geen andere SUBs en FUNCTIONs kunt definiëren. Dus geen lokale SUBs, helaas. Maar je kunt binnen een SUB wel een GOSUB-constructie aanleggen, d.w.z. een sprong naar een regel binnen de SUB en uiteraard ook terugkeer naar een lokatie binnen diezelfde SUB. Deze oplossing is in strijd met mijn eerste advies (blijf stijlzuiver), maar nood breekt wet. Zo hoog is die nood trouwens niet, ik geloof niet dat ik die truc werkelijk zou willen toepassen. GOTO, zal ik daar ook nog wat over zeggen? Ik ben te liberaal om GOTO helemaal in de ban te willen doen, zeker niet bij Basic, dat toch al zo beperkt is. GOTO is in een programma volgens de nieuwe stijl niet per definitie zo overbodig als GOSUB dat is. Wat ik echter in de discussie op het qb64phoenix-forum aan GOTO-voorbeelden zag, hoort in een programma nieuwe stijl beslist niet thuis. Enkele lezers zitten misschien nog te wachten tot ik eindelijk het in dit kader niet onbelangrijke onderwerp constanten eens ga aansnijden. Sorry, vandaag niet meer. Ik vind deze post nu al een beetje te lang uitgevallen.
Update 2024-09-02: Ik heb hierboven iets doorgestreept. Een definitie van een SUB of een DEF (het equivalent van wat in QB en LB een FUNCTION heet) is in True BASIC een declaratie, geen uitvoerbare code. De compiler leest de definitie in zonder zich iets van GOTO-, GOSUB- en RETURN-sprongen aan te trekken. Die hebben pas effect tijdens de uitvoering van het programma, na de compilatie. De eerste versie van Dartmouth BASIC uit 1964, meteen al een echte compiler, ging reeds zo te werk, en de meeste Basic-compilers en compiler/interpreters van tegenwoordig zullen dat ook wel doen. Maar GW-BASIC bijvoorbeeld, een nogal primitieve interpreter uit 1985, doet dat niet. Daar zal over een DEF FN heenspringen ertoe leiden dat de interpreter de definitie nooit te zien krijgt.
|
|
|
Post by Marco Kurvers on Sept 1, 2024 20:58:09 GMT
Bedankt Memophenon voor deze mooie uitleg. In Liberty BASIC werkt het net zo, op een paar aanpassingen na. - Er is geen LOCAL commando. Variabelen zijn altijd in SUB en FUNCTION definities lokaal. Wanneer variabelen globaal gebruikt moeten worden, dan is GLOBAL nodig bovenaan het programma. - De SUB en FUNCTION definities kunnen niet tussen de code staan. In Liberty BASIC kan dat alleen onder de uitvoerbare code staan. Dit geldt ook voor Q(uick)BASIC en QB64. - De parameters in SUB definities en ook de argumenten in CALL aanroepingen kunnen niet tussen haakjes staan.
Soms is een label wel nuttig als je GUI programmeert. SUB events kunnen geen gebruikers parameters hebben. Om variabelen niet overbodig globaal te maken, is er een oplossing. Maak de events als labels en roep de subroutines aan met CALL in het label-blok. Het voordeel is dan dat de extra variabelen alsnog meegegeven kunnen worden, omdat ze in het label zichtbaar zijn. Deze hulpmiddel heeft alleen nut in BASIC versies die GUI ondersteunen, zoals Liberty BASIC.
|
|
|
Post by memophenon on Sept 2, 2024 20:29:41 GMT
Over constanten
In de vorige post had ik een onderscheid gemaakt tussen twee bedoelingen die men met een variabele kan hebben: globaal gebruik over het hele programma, of beperkt tot een lokale rol in een beperkt segment (bij voorkeur een module natuurlijk) in dat programma. Er is nog een derde categorie: een eenmalige instelling van waarden ten behoeve van het gehele programma. Eenmaal een waarde toegekend, kan deze tijdens de uitvoering nooit meer worden veranderd. Dat is het idee. Een typisch voorbeeld hiervan is de waarde van het getal π in een programma dat met hoeken uitgedrukt in radialen werkt. In Q(uick)BASIC kun je dergelijke instellingen op deze manier realiseren: CONST PI = 3.14159265 Lang niet alle Basic-dialecten ondersteunen deze constructie. Maar als programmeur laten we ons natuurlijk niet kisten. Met een plaatsvervangende gewone globale variabele, zoals PI in dit geval, kunnen we hetzelfde effect bereiken. We hoeven alleen maar verderop in het programma nooit meer (knoop in je zakdoek!) de waarde te veranderen. Voorbeeld. De drie programma's in mijn vorige post passen een komma als datascheider toe. Je bent er niet helemaal zeker van dat dit de juiste keuze was. Dan kun je het derde programma als volgt aanpassen: !---- True BASIC program #4: constant, SUB, global and local variables ----!
LET S$ = "," ! Separator, meant as a constant
DIM A$(3) LET W1$ = "ALPHA" LET W2$ = "BRAVO" LET W3$ = "CHARLIE" LET W4$ = "DELTA" LET W5$ = "ECHO"
SUB CONCAT(L$, R$, P$) LOCAL C$ IF LEN(L$) = 0 OR LEN(R$) = 0 THEN LET C$ = "" ELSE LET C$ = S$ LET P$ = "<" & L$ & C$ & R$ & ">" END SUB
CALL CONCAT(W1$, W2$, A$(1))
CALL CONCAT(W3$, W4$, A$(2))
CALL CONCAT(W5$, W6$, A$(3))
FOR I = 1 TO 3 PRINT A$(I) NEXT I
END De globale variabele S$ fungeert hier als constante. Wanneer blijkt dat je huidige programma te veel kommaneukers aantrekt, kun je eenvoudigweg bovenin het programma een alternatieve stringwaarde zoals een spatie of een dubbele punt aan S$ toekennen, zonder je verder te hoeven bekommeren waar deze variabele zoal voorkomt in de rest van de code. Advies nummer 3 luidt daarmee: zet programmawijde instellingen bovenaan in je programma en vervang ze niet door lokale variabelen, zelfs niet als zo'n instelling in slechts één subroutine wordt aangesproken. Bij zowel mijn tweede als mijn derde advies draait het er dus om wat de bedoeling van een variabele is. Je bedoelingen zijn leidend, niet de techniek. (Technisch gesproken is het zelfstandigheidsniveau van de subroutine CONCAT nu gedaald van M2+ naar M2. Zie dat niet als een genante degradatie, maar als gebruikmaking van je vrijheden om je beperkte middelen op de beste manier in te zetten.) True BASIC en Liberty BASIC kennen geen constanten in bovengenoemde zin. Ietwat verwarrend is, dat in de documentatie van een of ander Basic-dialect gesproken kan worden van 'constante' waar in feite een letterlijke waarde ('integer literal', 'floating point literal', 'string literal') wordt bedoeld. Opletten dus bij het lezen. Dartmouth BASIC, True BASIC en de QB-familie (maar niet GW-BASIC) bevatten wel andersoortige constanten, die ik echter nooit zo heb zien benoemd: de namen van SUBs en DEFs/FUNCTIONs. Òf het herdefiniëren wordt als een compilatiefout bestempeld, òf de laatste definitie wordt als leidend voor het hele programma aangenomen. In het laatste geval kan het programma weliswaar worden gecompileerd en uitgevoerd, maar wijzigt de waarde tijdens de uitvoering dus niet. Nooit, nimmer meer.
|
|
|
Post by frizhd on Sept 3, 2024 15:07:50 GMT
Wat een moeilijke materie, MM. Mijn niveau is M=0 en ik snap hier niets van. Wist je dat er mensen zijn die met Python ‘appels’ met ‘peren’ kunnen vermenigvuldigen en dan komt er ‘Fruit’ uit? Werkt CONCAT heus in TrueBasic? Wat is de zin van dat programmaatje? Kun je me dat nog eens uitleggen? Iets eenvoudiger graag, zodat ik het ook kan snappen. Wat moet die routine, module opleveren? Dank.
|
|