Logistics
- Roll
- Comments/Questions?
Arrays
(We addressed arrays and repetition last time. Will do files today). Comments/Questions/Wonderings?
Repetition is probably the one thing that makes computer programs as useful as they are because it allows us to avoid having to write code for each tiny thing that happens. You can certainly repeat simple actions, but the real power comes from repeating the same action on similar kinds of data. That data can come from the user, be an item in an array or be a line from a file. We'll start with arrays.
An array is a collection of data items. The collection has a name (the declared variable) and items in the array are referred to by using an index. This is very similar to strings that have positions of each individual character. For example, we might have a set of scores. As with other data, we have to declare array variables and we do so in a manner similar to what we have done before, i.e.,
Dim scores(20) As Double
What this declaration does is to set aside space for a number of scores. How many scores? ... 21 In Visual Basic, when we declare an array we indicate the last/greatest valid index. Since, as with strings, the first element is at position 0 and the last valid position or index is 20, we have 21 items in the array. However I tend to avoid using the zeroth element unless there is good reason to.
As noted above, there is much power in arrays and repetition. For example if we want to assign each item in the array a random value between 0 and 100 we could use the following code.
Dim scores(20) As Double Dim i As Integer For i = 1 to 20 scores(i) = Fix(Rnd() * 101) Next i
If we had 1,000 or 1,000,000 items the only change in the code would be the italicized values. And if we wanted to get the average of the scores (after initializing the array of scores), all that is necessary is:
Dim sum, average As Double sum = 0 For i = 1 to 20 sum += scores(i) Next i average = sum / 20
Similar code could be used to process 100 values or 1,000 or 1,000,000. We'd have to change the number we divide by to calculate the average. Questions? Comments? wonderings? The code below would allow us to do this with an indeterminate number of scores, perhaps entered by the user.
Dim scores() As Double Dim i As Integer Dim count as Integer = InputBox("How many scores?") Redim scores(count) For i = 1 to count scores(i) = Fix(Rnd() * 101) Next i Dim sum, average As Double sum = 0 For i = 1 to count sum += scores(i) Next i average = sum / count
Shuffling cards example
If you were doing a card game program (e.g., poker) you would need to represent the deck of cards and have a means for shuffling the deck. A common approach to this task is to load an array with integers representing the cards. We need space for 52 cards (e.g., Dim deck(51) as Integer
. To initialize the deck of cards we'd use a simple For loop.
For i As Integer = 0 to 51 deck(i) = i Next i
To shuffle we will use a very clever and neat little trick. We'll just randomly swap cards but do so an organized way. We will swap a random card with the "last" card, then reduce last by 1 and do it again and then repeat that process. The code would look something like:
Dim temp, posit, last As Integer For last = 51 to 1 Step -1 posit = Fix(Rnd()*last) temp = deck(last) deck(last) = deck(posit) deck(posit) = temp Next i
Questions? Comments? wonderings? Note that swapping the values required the use of a temporary variable. We cannot just assign one variable the value of the other and then assign the value of the second the value of the first. That would make them have the same value.
Files
When thinking of using files, perhaps the most important idea is to recognize that files need a pattern that is known (or created in them) so you can access the data appropriately for processing. I like to think of files as having a bunch of lines and each line has a particular pattern followed by the values. For example, there could be commas separating each of the values of interest on each line. Or there might be only one value per line. Or the data might look like 123456East,James Philip*Graded#3.00"Humanities Arts & Sciences--CS Teaching!Senior^
which will allow us to read a line and then reliably pick of each individual piece of data on the line. We could describe the data as:
- student id: first six characters on the line
- name: string value starting at position 6 and continuing for the number of characters equal to the position of the asterisk minus 6 minus -1 (I think)
- enrollment category: characters starting after the asterisk and continuing until the number sign (position of # minus position of * minus 1, I think)
- credits: numeric value of characters starting after the number sign and continuing until the double quote character (you can figure the substring data to get it)
- major—college and major name: value starting with the double quote and continuing to the exclamation point
- year/level
- status note
When you plan for files you will need to think about the data that is needed and how it will be organized. If you are planning for you own data, just decide. If you are using someone else's data, you will have to figure out its organizational scheme.
An Example
The task I am going to program is to calculate the standard deviation of a set of scores. The scores are in a file with one number on each line. The number on the first line indicates how many scores are to be processed. When I get done I will write the data and standard deviation out to a different, new file. Also, I use a menu item to allow the possibility of creating a file of values to work on.Any questions or wonderings?
The Code
The code for the program is given below. Most of the code will be for a button handler and either directly or indirectly use subroutines. There are some class variables that allow for easy communication between the subroutines. There is also the code to have the program create the data file which calls for the use of the Randomize
method.
'........... Class variables Dim scores(100), sum, scoreCount As Integer Dim mean, stdDev As Double Dim reptSummary As String Private Sub ReadScoresAndCalculateMean() Dim scoreReader As IO.StreamReader Dim fileName As String Dim i As Integer OpenFileDialog1.Title = "File of numbers to open" If OpenFileDialog1.ShowDialog = DialogResult.OK Then fileName = OpenFileDialog1.FileName scoreReader = IO.File.OpenText(fileName) scoreCount = Val(scoreReader.ReadLine()) If scoreCount > 100 Then 'increase array size if needed ReDim scores(scoreCount) End If sum = 0 i = 1 Do Until i > scoreCount Or scoreReader.EndOfStream scores(i) = Val(scoreReader.ReadLine()) sum += scores(i) i += 1 Loop mean = sum / scoreCount scoreReader.Close() End If End Sub Private Sub CalculateStandardDeviation() Dim sum As Double sum = 0 For i = 1 To scoreCount sum += (scores(i) - mean) ^ 2 Next stdDev = (sum / scoreCount) ^ 0.5 End Sub Private Sub ReportResults() Dim reptSummary As String reptSummary = "count:" & vbTab & scoreCount.ToString & vbNewLine _ & "average:" & vbTab & mean & vbNewLine _ & "std.dev.:" & vbTab & stdDev.ToString MessageBox.Show(reptSummary) ' ............... Record results on file Dim resultWriter As IO.StreamWriter resultWriter = IO.File.CreateText("results.txt") resultWriter.WriteLine(reptSummary) For i = 1 To scoreCount resultWriter.WriteLine(scores(i).ToString) Next resultWriter.Close() End Sub Private Sub CreateToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles CreateToolStripMenuItem.Click Randomize() Dim hi, lo, count, randomVal As Integer count = InputBox("How many random values?") lo = InputBox("Lowest possible random value?") hi = InputBox("Highest possible random value?") Dim fileName As String = InputBox("File name?") Dim output As IO.StreamWriter output = IO.File.CreateText(fileName) output.WriteLine(count) For i As Integer = 1 To count randomVal = Fix(Rnd() * (hi - lo + 1)) + 1 output.WriteLine(randomVal) Next i output.Close() End Sub Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click ReadScoresAndCalculateMean() CalculateStandardDeviation() ReportResults() End Sub
Discussion about files, arrays, user-defined subroutines, file dialogs, & menustrip usage is provided below. The information refers to the code above. Ask questions about anything that is not clear.
Files
There are four steps in using a file—1) declare a variable for accessing the file; 2) open the file; 3) use the file (read from or write to it); 4) close the file. Those are discussed in more detail below.
- declare a file
Files have internal and external names. External names are what we seen in our folders. They also contain the "path" information. In VB, there is a default and easy to use location for files — the
bin/Debug
folder. I suggest you always use that (in our class at least). Otherwise, your program may not work if it is run on someone else's computer.Declaring a file typically includes preparing variables for the internal file name and the external file name. The internal name will be a
StreamReader
or aStreamWriter
depending on whether you are reading from or writing to a file. We will be doing both. The external file name is a string that contains the name of the (it can be a variable or a literal, we'll use a variable). Often, the programmer will know in advance what the files are to be called and the following code will be applicable.Dim scoreReader As IO.StreamReader fileName = "scores.txt" ... Dim resultWriter As IO.StreamWriter
Sometimes, though, you will want to allow the user to identify the filename. You could do so by asking for the name of the file and using a program statement like:
fileName = InputBox("File name?")
Or, alternatively, you can allow the user to browse for the file by using a file dialog. The following code provided that capability.
OpenFileDialog1.Title = "File of numbers to open" If OpenFileDialog1.ShowDialog = DialogResult.OK Then fileName = OpenFileDialog1.FileName ... 'whatever else is done if a file was selected by the user End If
To use this you have to add an
OpenFileDialog
control to your form. (It doesn't show on the form but is included below it in the Visual Studio window. This allows the user to browse to find and select a file. TheIf
statement is necessary to avoid doing something when the user does not select a file. Also note that the openfiledialog control has a property that contains the name of the file the user selected. Questions/Comments/Wonderings? - open the file
Opening a file for reading uses the
.OpenText()
method (as shown below). Opening a file for writing use the.CreateText()
method (shown below) when you want a new file or to replace an existing file. If you want to add text to a file instead of creating a new file, use the.AppendText()
method. All these methods require that you supply the external file name as a parameter to the method.scoreReader = IO.File.OpenText(fileName) ... resultWriter = IO.File.CreateText("results.txt")
- get data from (or put data into) the file
To use the file requires reading data from it (into some variable) or writing data to it from a variable or by building the string you want to put in the file. Keep in mind that the file consists of text. On input, VB will convert the string values to numbers according to its rules. I assume numbers would also be converted to text on output (but recommend you use the
ToString
method. I use theVal()
andToString
methods (because I have a touch of OCD).scores(i) = Val(scoreReader.ReadLine()) ... resultWriter.WriteLine(scores(i).ToString)
- close the file
When you are done reading or writing the file, you should always close it. The
.Close()
method works for files being read from and files being written to.scoreReader.Close() ... resultWriter.Close()
Questions/Comments/Wonderings?
Arrays
An array is a collection of values. Each value must be of the same type, i.e., you can have an array of Integer
values, an array of String
values, an array of Double
values, an array of TextBox
values, an array of RadioButton
values, etc. As with other variables you have to declare arrays and you do so with a Dim
statement, e.g.,
Dim scores(100) As Integer
The above code indicates we have a variable called scores and the last valid index is 100 — thus we have 101 values: score(0)
, score(1)
, score(2)
, ..., score(99)
, score(100)
. In the declaration, the 100 is the last valid index or subscript. I typically ignore the 0th location. A subscript indicates which of the values you are using. Subscripts can be any expression that evaluates to an integer.
You use a subscripted variable just like any other variable—just be sure that you are using the particular value from the collection that you wish, i.e., that the subscript has the correct value. (You never, or almost never, use the collection variable without a subscript.) In our example (in the subroutine) we store a value in the array, then add that value to a sum, and we take care to increment the index/subscript so we don't put a new value in the same spot.
scores(i) = Val(scoreReader.ReadLine()) sum += scores(i) i += 1
Once we have the values in the array, we can use them to do most anything we want. In our case we want to calculate the squares of the differences between each score and the mean and we later want to print the scores as part of our report.
For i = 1 To scoreCount sum += (scores(i) - mean) ^ 2 Next ... For i = 1 To scoreCount resultWriter.WriteLine(scores(i).ToString) Next
Remember that we used Dim
to declare and indicate the size of the array. You can change that value while the program is running by using Redim
. This allows us to make the array bigger if needed. If the array already have some values stored in it we would need to use ReDim scores(scoreCount) Preserve
.
scoreCount = Val(scoreReader.ReadLine()) If scoreCount > 100 Then 'increase array size if needed ReDim scores(scoreCount) End If
Questions/Comments/Wonderings?
When you are processing an array of values you need to be careful to not go try to access a position in the array that is not there. Thus, there are two conditions that could cause the end of the repetition for reading the values in the array. We might have read the number of lines indicated in the first line in the file or we might have come to the end of the file. The .EndOfStream
property will have either a value of true or false depending on whether there is no more data.
Questions/Comments/Wonderings?
Modularization via Subroutines (& Functions)
We have been using subroutines in all our code, but the VB system automatically generated them for us when we clicked on a button in the form or on the form itself. When you create your own subroutines it is like writing a new instruction for the computer. You name the new instruction and tell the computer how to do it. For example, when I thought of doing this program the main part of the code seemed to consist of three tasks: 1) read the scores; find the standard deviation; 3) report. In thinking about those things I knew that I had to store all the values in the array and that I would need to add them all up (to find the mean). I decided to do those two things at the same time instead of having two loops to do them separately. So, at the more abstract or general level my task was to:
ReadScoresAndCalculateMean() CalculateStandardDeviation() ReportResults()
Since that was the way I thought about the problem, I just left the solution that way and planned to create a subroutine for each of those tasks. That way (six months from now) I can easily see how I went about the problem in general and can still look at the details in the subroutines if I want to see more specifically how the problem was solved.
Questions/Comments/Wonderings?
A subroutine can access data in three ways:
- local variable declared inside the subroutine
- values passed in as parameters
- class variables
Be careful when naming variables. If two variables have the same name, only the most local one can be used. That is usually what is wanted but not always.
Questions/Comments/Wonderings?
Menu strips
One way to get a file of values is to create it with a text editor, then place it in the bin | debug
folder. You could also use a spreadsheet to automatically generate your random numbers. You would then save the file as a .csv
(comma separated values) file. (And still place in the the bin | debug
folder.) Another alternative is to have your program generate the file. I added that capability to this program using a menu.
To use a menu, you merely add the menustrip control to your form. When you do so, the menustrip control will appear at the bottom of the project window (like the openfiledialog did). But a menustrip also appears at the top of the form and you are allowed to enter names for menu items and sub-menu items. I chose to use File and Create as menu names. When I wanted to produce code that should get executed when I select the Create menu item, I just double-click it and the subroutine is automatically generated (as if I had done a button). Then, it is just a matter of developing the code to solve my problem. In this case:
Randomize() Dim hi, lo, count, randomVal As Integer count = InputBox("How many random values?") lo = InputBox("Lowest possible random value?") hi = InputBox("Highest possible random value?") Dim fileName As String = InputBox("File name?") Dim output As IO.StreamWriter output = IO.File.CreateText(fileName) output.WriteLine(count) For i As Integer = 1 To count randomVal = Fix(Rnd() * (hi - lo + 1)) + 1 output.WriteLine(randomVal) Next i output.Close()
Questions/Comments/Wonderings?
Next Time
- sharing data between forms