Pages

8. Arrays Part 2



Arrays are often used in programming so it's important to fully understand how they work.  In the last post I mentioned a one dimensional array for holding Fender guitar serial numbers.  Owen, a prodigious FreeBASIC programmer and FreeBASIC regular, has provided the following code for the 1 dimensional array example.

dim as integer i

dim as integer fender(3)

fender(1)=1000

fender(2)=877

fender(3)=456

for i = 1 to 3

    print "fender guitar serial #";i;" is worth $";fender(i)

next


sleep

Let's see what is happening.  The first line sets up an integer variable, i,  and the next line sets up an integer array variable that can hold three items, fender(3).  The next three lines assign values to each of our array locations.  This is then followed by a for next loop, we've seen how this works in a previous post, that prints out the contents of each array variable one line at a time.  The sleep command pauses the program so we can see the output.  If we don't sleep the world flashes before our eyes before we an make any sense of what is happening.

The output follows:


The second example I mentioned  was about a typical suburban street with a row of houses, each with a numbered letter box. Again Owen has provided some example code to demonstrate the 2 dimensional array.


dim as integer x,y

dim as string house(2,4) ' first name, last name, box number, street name

house(1,1)="Frank"

house(1,2)="Tic"

house(1,3)="123"

house(1,4)="Elm Ave"

house(2,1)="Mo"

house(2,2)="Jo"

house(2,3)="456"

house(2,4)="Oak Street"

for x=1 to 2

     print house(x,1)

     for y=1 to 4

          print house(x,y);" ";

     next

     print

next

sleep

Let's work through this code.  The first line creates two integer variables, x and y.  Note how both variables are created on the same line.  This line is followed by 
dim as string house(2,4) ' first name, last name, box number, street name
In this line we create a string array that is 2 by 4 in size. Items after an apostrophe are not used by the FreeBASIC interpreter but are valuable comments for us to understand the program.  So first name, last name, and so on remind us about the structure of the house(2,4) array.

The next eight lines of code that  look like:
house(1,1)="Frank"
load the array. You can see that the first dimension of the array defines the house, while the second dimension hold a specific attribute of the house, such as the owners first name, their last name, the box number, and the street name.

Finally we have the nested for next loops that work their way, by using the x and y variables, through each house and attributes.

The output follows:

Variables are commonly used in programming and it is worth the trouble of trying a few examples for yourself to really understand how they work.

A big thanks to Owen for his code and his feedback on the FreeBASIC forum:  http://www.freebasic.net/forum/index.php   If you haven't had a look, please go ahead, you will find very knowledgeable programmers on the site who are very generous with their support and feedback.

If you want an example of what can be done with FreeBASIC, you need only go to Owen's site, http://fbcadcam.com/ to see an impressive graphical program written entirely in FreeBASIC!

As always, please feel free to leave a comment and see you in the next post.

9. Random Phrase Generator


Creativity block is crippling.  You can sit in front of a blank screen for hours, invariably you end up browsing the web or hopping from YouTube clip to YouTube clip. Some time ago I read a suggestion that if you open a dictionary at a random page then, with your eyes closed, choose a word on the page  you could use that as a starting point or trigger for whatever you were doing. For instance, you are interested in writing a book about writer's block.  Using the dictionary method you choose the word "idol" (this was genuinely picked randomly).  Thinking about this word you may think of who your writing idol's are and how they solved their writer's block problem.  And you are off.

What has that to do with FreeBASIC programming?  Well I thought we can do better than a random dictionary word.  How about a whole phrase made up of a verb, and adjective, and a noun. First off we need our list of words.  I found the 100 most frequently used English verbs in https://www.espressoenglish.net/100-most-common-english-verbs/  (the original source is http://corpus.byu.edu/coca/ but you have to play around with the interface a bit.)   The same web site will give you the adjectives and nouns.  I have done the search for you so you don't have to; keep the thanks until later :-).

The following program can be typed into your IDE or simply cut and paste.

'==================================
'Phrase
'module for returning a
'verb adjective noun phrase
'
'Franktic
'
'FreeBasic
'==================================

 Dim asVerb(1 to 100) as String
 Dim asAdjective(1 to 100) as String
 Dim asNoun(1 to 100) as String

 Dim sInputString as String = ""
 Dim iLocation as Integer = 0

 Dim iIndex as Integer = 0


 '==================
 'Fill asVerb array
 '==================
 sInputString = "be have do say go can get would make know will think take see" _
     + " come could want look use find give tell work may should call try ask" _
     + " need feel become leave put mean keep let begin seem help talk turn start" _
     + " might show hear play run move like live believe hold bring happen must" _
     + " write provide sit stand lose pay meet include continue set learn change" _
     + " lead understand watch follow stop create speak read allow add spend grow" _
     + " open walk win offer remember love consider appear buy wait serve die send" _
     + " expect build stay fall cut reach kill remain "

 for iIndex = 1 to 100
     iLocation = Instr(sInputString, " ")
     asVerb(iIndex) = Left(sInputString, iLocation)    
     sInputString = Mid(sInputString, iLocation + 1)
 next

 '==================
 'Fill asAdjective array
 '==================
 sInputString = "other new good high old great big American small large" _
     + " national young different black long little important political" _
     + " bad white real best right social only public sure low early" _
     + " able human local late hard major better economic strong possible whole" _
     + " free military true federal international full special easy clear recent" _
     + " certain personal open red difficult available likely short single medical" _
     + " current wrong private past foreign fine common poor natural significant" _
     + " similar hot dead central happy serious ready simple left physical general" _
     + " environmental financial blue democratic dark various entire close legal" _
     + " religious cold final main green nice huge popular traditional cultural "
         
 for iIndex = 1 to 100
     iLocation = Instr(sInputString, " ")
     asAdjective(iIndex) = Left(sInputString, iLocation)
 '    print asAdjective(iIndex)
   
     sInputString = Mid(sInputString, iLocation + 1)
 next

 '==================
 'Fill asNoun array
 '==================
 sInputString = "time year people way day man thing woman life child world school" _
     + " state family student group country problem hand part place case week" _
     + " company system program question work government number night point home" _
     + " water room mother area money story fact month lot right study book eye" _
     + " job word business issue side kind head house service friend father power" _
     + " hour game line end member law car city community name president team minute" _
     + " idea kid body information back parent face others level office door health" _
     + " person art war history party result change morning reason research girl guy" _
     + " moment air teacher force education "

 for iIndex = 1 to 100
     iLocation = Instr(sInputString, " ")
     asNoun(iIndex) = Left(sInputString, iLocation)

     sInputString = Mid(sInputString, iLocation + 1)
 next

'============================================================
'print out a phrase made up of a random: verb + adverb + noun
'============================================================

 Randomize
   
 for iIndex = 1 to 10
     Print asVerb(int(Rnd * 100) + 1) + " " + asAdjective(int(Rnd * 100) + 1) _
         + " " + asNoun(int(Rnd * 100) + 1)
 Next

Sleep

Let's go through the program to see how it works.  The first section is for allocating variables.  We have set up three arrays of 100 strings each, one each for our verb, our adjective, and our noun.  The other variable sInputString will hold a string made up of each separate word separated by spaces. The iLocation variable will be used to find the spaces separating the words within the sInputString. And the iIndex variable will be used for working through our arrays.

Three similar sections follow.  Let's work through the first one, the one with the Fill asVerb array comment. The first part of this block assigns the separate verbs into the sInputString variable. To do this I have put enough words to fill a line then added the underscore character ( _ ) at the end. This character tells FreeBASIC that the line is not finished but continues.  The start of the next line starts with a plus character ( + ).  This character concatenates the following string with the previous string.

We now have sInputString made up of the 100 most common verbs separated by spaces.  The next bit of code needs some explanation.

 for iIndex = 1 to 100
     iLocation = Instr(sInputString, " ")
     asVerb(iIndex) = Left(sInputString, iLocation)    
     sInputString = Mid(sInputString, iLocation + 1)
 next

The first line will loop the for...next block from 1 to 100 and keep the iIndex variable updated.  The second line assigns the location of the first space, " ", in the string to the iLocation variable.  The Instr() function is built into FreeBASIC and takes the string to be searched (sInputString in this case) and the string we are looking for ( " " - ie the space character) as its parameters. It returns the numerical position of the first instance of the found string.

The next line then uses the inbuilt Left() function assigns the string to the left of iLocation, the location of the first space, the variable asVerb(iIndex).  On the first pass it will allocate the first verb "be" to asVerb(1).

The next line takes our sInputString and assigns it a new string made up of the original string but starting from the second word.  The Mid() function takes a source string, sInputString in this case, and a location within that string and returns a new string made up of the source from the location to the end of the string.  In our case the location is the position following the space after "be".  So sInputString will now be the same as before however we have dropped off the first word and space.  It now begins with "have".

The process is looped 100 times so sInputString progressively drops off the first word and space and commences with a new word.  That new word is then assigned the asVerb array before it too is dropped off.

The adjectives and nouns are handled in exactly the same manner and so we end up with three arrays: one for the verbs, one for the adjectives, and one for the nouns. In a previous version I had this part of the program read the words from three separate text files.  I have opted to show self contained version here but feel free to try that way for yourself.  The benefit for the three file version is that you can easily change any of the words with text editor without having to recompile the program.  The benefit of what I have shown above is that the program is self-contained and probably runs faster since it doesn't have to read external files.

The last block of the program prints out 10 phrases made up of random verb-adjective-noun combinations.  If you want some more practice you can change the number of phrases produced or send the phrases to a text file so you can print them out at your leisure.  You can try this idea with other language parts. Or even different languages.

That's all there is to it.  Not too difficult but I find the phrases very useful for coming up with creative ideas.  Let me know if you use this routine what you use it for.

As always, please feel free to leave a comment and see you in the next post.

10. Writing Large Programs

When you are writing a small program and not collaborating with anyone else, it is quite acceptable to fire up your favourite editor and type away in the one code file.  It is not unusual to see programs that span several hundred lines of code in one file. If the code has been well commented, then there is no problem.

If you are working on a program with a group of people, or you think the program is going to be much longer than 100 lines (this is not a hard and fast rule - you can determine when a program is too long), you should consider breaking your program into modules.

In this post I will show you how I write large programs into manageable modules.  The program I will work with is not particularly exciting but the process of writing is important.  We'll write a program that gives the user the option to add two number, give a random number, or print a greeting.

I've created a new code file called LargeProgram.bas in its own directory.

Let's begin with a simple menu:

/'
======================
Large Program Example in FreeBasic
Franktic
3 June 2017
======================
'/

Dim iOption As integer

iOption = 0

Do
Print "What would you like to do: "
Print"     1.  Print a Greeting"
Print"     2.  Give a random number"
Print"     3.  Add two numbers"
Print"     99. Quit"
Print
Input iOption, "Please enter a menu item number: "

Loop Until iOption = 99

All of this should be pretty obvious, but if not then look at some of the earlier posts.  The code presents a simple menu and the user is asked to enter an option number.

Let's add code to handle the chosen option.  Please note I have shown the additional code in blue to see where it fits into the main program:

Input iOption, "Please enter a menu item number: "

Select Case As Const iOption
Case 1
Print "selected option 1"
Case 2
Print "selected option 2"
Case 3
Print "selected option 3"
Case 99
Exit Select
Case Else
Print "Not an option"
End Select

Print
Print
Loop Until iOption = 99

I have decided to use the Select Case statement to process the option ( http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgSelectcase ) .  Notice that the program is still extremely simple but we give get feedback while testing the program that the option chosen is actually being processed.  The line with Case 99 exits the Select statement, prints two blank lines, then returns control to the Loop Until iOption = 99 line which effectively ends the program.
Another point to note is the Case Else statement.  This catches all invalid inputs and gives an error message.

Let's begin with the simplest of the options, print a greeting.  In your editor begin a new code file called greeting.bas and save it into the same location as your main file above.  Add the following code:
/'
======================
Greeting in FreeBasic
Franktic
3 June 2017
======================
'/
Declare Sub pGreeting

Sub pGreeting
Print "Hello"
End sub

In the Fundamental posts we had a look at functions, blocks of code that return a value.  In this case we don't need a value returned, we want the block of code to simply print Hello then pass control back.  Functions that do not return values are known as Procedures.  In FreeBASIC these are called Subs (? short for subroutine)  http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgSub.  Subs need to be declared as shown above.  This is followed by the code for the sub, in this case it simply prints Hello. I have prefixed the name with a p for procedure to avoid clashes with existing FreeBASIC words.

To use the new procedure we need to tell our main program to include the code, then we can simply call it by using its name.  To include the code we add the following line:

#Include once "Greeting.bas"

I typically place all the #Include lines together at after the title comments.  Once I open a program file I can see what other files are needed.  The 'once' qualifier tells FreeBASIC to include the code only once even if other code modules call Greeting.bas.

Now to call it we change the code in the case statement to say the following, the new line is in blue:

Case 1
pGreeting
Case 2

If we now compile and run the program, pressing option 1 gives us a Hello.

Let's now tackle option 2, give a random number.  We saw the code for getting a random number between 1 and 100 in the post about random phrases.  We'll use the same idea here.  Because we expect a result back we will not use a procedure; we'll use a function instead.  To add even more functionality to the code we will allow the user to add the highest random number.

As we did earlier, create a new code file in the same directory as our other two files and call it random.bas.  Cut and paste the following code:

/'
======================
Random in FreeBasic
Franktic
3 June 2017

Usage: fRandom(limit)
Returns: random integer between 1 and limit
======================
'/

Declare Function fRandom(limit As Integer) As Integer

Function fRandom(limit As Integer) As Integer
Randomize

Return Int(Rnd * limit) + 1
End Function

The fRandom function is similar to the pGreeting procedure.  Where they differ is our function accepts an integer parameter (procedures can accept parameters if required) , the limit integer, and returns an integer value when it is called.  Compile the file to check for errors.

In the main program you will need to include the file and allocate a new variable to hold the upper limit for our random number.  The blue lines is the new code:

#Include once "Greeting.bas"
#Include once "Random.bas"

Dim iOption As Integer
Dim iTopNumber As Integer


To call the function we add the code in blue:
Case 2
        Input "What is the highest random number that can be presented? ", iTopNumber
Print fRandom(iTopNumber)
Case 3

The final option adds two numbers.  Again start a new code file in the same directory and call it AddTwo.  Cut and paste the following code:
/'
======================
AddTwo in FreeBasic
Franktic
3 June 2017

Usage: fAddTwo(a, b)
Returns: a+b integer
======================
'/

Declare Function fAddTwo(a As Integer, b As Integer) As Integer

Function fAddTwo(a As Integer, b As Integer) As Integer
Return a + b
End Function


This should be making sense now.  We declare the function and then we write the function code.  In this function we are accepting two parameters and returning the integer sum.

In the main program we add the #include and the two new variables (in blue):
#Include once "Greeting.bas"
#Include once "Random.bas"
#Include Once "AddTwo.bas"

Dim iOption As Integer
Dim iTopNumber As Integer
Dim iFirst As Integer
Dim iSecond As Integer

We also call the code in the Case statement:

Case 3
Input "What is the first number? ", iFirst
Input "What is the second number? ", iSecond
Print "Sum is ";
Print fAddTwo(iFirst, iSecond)
Case 99

That's it for the coding. Getting back to what I mentioned at the start of this post, you can see that our main program is now compact and quite clear.  The program was built up with simple statements and, as each module was written it was added to the main program.  This allows us to test our program as it is being built. It is easier to work on small sections, without the distraction of hundreds of lines of interdependent code.

If I was collaborating with others on this program, I could have asked a colleague to write  a random number generator that took a limit and returned a random number up to that limit.  I could keep writing the the main program, placing a 'stub' where the function belonged, then include their code once it was written.  I could even write a dummy function that simply printed a "hello from dummy function xyz" in preparation.  Large programs are usually written by teams of programmers, each working on specific functions.  Given clear input parameters and equally clear returns, along with good documentation, large programs become can be put together rapidly.  Blocks of completed and tested code references replace stubs to become part of a larger program.

A further advantage of this modular style of programming is that the functions and procedures that sit in separate files can be used in any number of programs we write.  You simply need to include them and call them. This highlights the importance of good documentation in the header of the modules so it is clear what parameters they require and what is returned (in the case of functions). For instance, any program I write going forward that needs a random number can reference the block we saw above.

If clarity, collaboration, and reusability of code were not enough, breaking code into separate files allows for easier programming.  Each code file can be thoroughly tested to ensure it is bug free and working correctly.  It can then be put aside and called as needed, knowing that only in exceptional circumstances do you need to revisit the code.

Although the include files above have the usual .bas extension, they could have had a .bi extension instead indicating that they are basic include files and not normal code files.  Follow the link to the #include section of the wiki for more information:  http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgInclude .

One final point to note: The example include files we used, each perform one simple action.  Include files can include collections related functions and procedures that can extend the functionality of the FreeBASIC language.  Libraries of include files exist that add complex capabilities, such as performing graphics or database work, not present in the base language. You can even include other programming languages, such as Lua, into FreeBASIC code!  Here is a list of external libraries to explore  http://www.freebasic.net/wiki/wikka.php?wakka=ExtLibTOC .
As always, please feel free to leave a comment and see you in the next post.

11. Reading and Writing Integers from a File

A recent comment to Post 6 asked: I have a FreeBASIC program which writes just over 100,000 integers to a file. I would like to see an example of how to read such a file.

In this post we will create a file with 100,000 random integers then show how we can read them back and and manipulate them.

Let's start by writing our header:


/'
======================
Integer File read in FreeBasic
Franktic
25 July 2017
======================
'/


Next we will declare some variables.  I was taught to always initialise variables at declaration and, as all the variables are integers, let's set them all to zero.  

Dim As Integer myFile = 0  ' file handle
Dim As Integer x = 0       ' loop counter
Dim As Integer iSum = 0    ' will hold sum of integers selected
Dim As Integer iBig = 0    ' will hold the largest integer of those selected
Dim As Integer iSmall = 0  ' will hold the smallest integer of those selected

We will also declare an array to hold the integers we read back from the file.  Note, the array has to have enough items declared to hold all the integers read back.  If we declare our array to go from 1 to 1000 and we try and store more than 1000 items the program will crash.

Dim As Integer aInt( 1 To 100000)

As we saw in the two 'Fundamental' posts, we need to generate a file handle using the FreeFile() command and assign it to our myFile variable.  We can then Open the file for Output since we want to write the integers.

/' Generate Integers and write to file '/ 
myFile = FreeFile()
Open "test.txt" for Output as #myFile

This next section of code initialises the randomize command then enters a loop that will cycle 100,000 times.  With each pass it prints the loop number so we know the program is working and generates a random number between 1 and 100 (like throwing a 100 sided dice, this rnd function is explained in a previous post) which it writes directly into the file we created above.  At the end of for...next loop we close the file.

Randomize, 1

For x = 1 To 100000
'show where we are up to
Print Str(x)
Print #myFile, Str(Int(Rnd*100)+1)
Next

Close #myFile

We add two lines to tell us that the writing has completed.  The Sleep command waits for the user to press a key before it continues.  Adding a break such as this in a program is useful for finding errors (debugging).  Once you are happy the program is working correctly you can get rid of these lines or simply comment them out.

Print "Integer file written.  Press any key to continue."
Sleep

Now to read the integers back.  First of we clear the screen with the Cls command and open the file for Input - we are reading, input, from the file - not writing, output.  Using the Input command in the for loop we read each integer directly into our integer array declared earlier. And, like before, we close the file once we are done.

Cls
' Read Integers from file into array aInt()
Open "test.txt" for Input as #myFile

For x = 1 to 100000
Input #myFile, aInt(x)
Next

Close #myFile

To give an example of what we can do with the array of random integers we have just read, we will focus on the first 10 integers in the file, now in the array.  You can choose a different set of integers or the entire 100,000 if you want.  Let's  calculate the sum of the 10 integers, then find the largest integer and the smallest integer.

First off we initialise the iBig and iSmall variables to the first integer in our array.

iBig = aInt(1)
iSmall = aInt(1)

The for...next loop will cycle 10 times and add the current array integer to the iSum variable.  At the end of the loop we will have added all 10 array integers to iSum and have our sum.

The statement starting with If iBig compares the contents of iBig with the current array contents.  If iBig is smaller, meaning the array contents is bigger, then we set iBig to the larger integer.  If iBig is the same size or larger then this bit of code is skipped.  At the end of the for...next loop iBig will hold the largest integer in the set we selected.

The statement starting with If iSmall performs a very similar operation however, at the end of the for...next loop,  it will contain the smallest integer of the set we selected.

For x = 1 To 10
iSum = iSum + aInt(x)
If iBig < aInt(x)Then
iBig = aInt(x)
EndIf
If iSmall > aInt(x)Then
iSmall = aInt(x)
EndIf
Next

Now we have a small section that displays what we have done and...that's all folks.

Print "The sum of the first 10 numbers read is " + Str(iSum)
Print "The largest of the first 10 numbers read is " + Str(iBig)
Print "The smallest of the first 10 numbers read is " + Str(iSmall)

Sleep
End

You can cut and paste each of the sections of code above into your FreeBasic editor and run it or,  better still, type it out by hand.  Typing out by hand actually gives you a bit of practice in using the editor and, if you make a typing mistake, it will help you learn to read your code and find errors.  If you type it you may also be encouraged to try modify the code to see the effect of your changes.

When I wrote this short program I didn't want to wait for 100,000 integers to be generated (although in the end it didn't take terribly long) so I started with generating 100.  I set up the array and the loops to deal with 100 integers.  Then, when I modified the program so that it would generate 100,000 integers I changed the loops but forgot the array dimension.  I could not understand why the program was crashing after it generated 100 integers. This was a simple bug that was easily fixed but one that I will look out for in future programs.

Anyhow, I hope this post has been useful.  Please feel free to leave a comment and see you in the next post.