Unit VI Additional Topics

CS 1130 Visual Basic—Fall 2017

Day 21 — Unit VI (Additional Topics)

Files, Arrays, Subroutines

Logistics

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:

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.

  1. 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 a StreamWriter 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. The If 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?

  2. 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")
    
  3. 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 the Val() and ToString methods (because I have a touch of OCD).

        scores(i) = Val(scoreReader.ReadLine())
        ...
        resultWriter.WriteLine(scores(i).ToString)
    
  4. 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:

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

Day 22 — Unit VI (Additional Topics)

Sharing Data Between Forms

Logistics

Project

If you have not yet done so, prepare your project proposal and schedule a meeting with me.

Those who have submitted a proposal and met with be, be working on the project and preparing for the project check-in meeting. That should happen in the next 2-3 weeks or as soon as you have completed enough of the work (see the description).  Any comments, questions, wonderings?

Sharing Data Between Forms

VB forms are able to share data but only in particular ways. Hopefully, the material below and the sample program in the notes directory (DataSharing.zip) will provide guidance as to how you can make your forms share data. I show several techniques, so be careful as you read about and try to use any of them.

Forms after the "start" form

Typically the start form is one of a kind and is accessed using its "name". Any form can access the start form's controls and public class variables. Our example is of a form that can add something to the start form's listbox and change values of its variables, e.g.,

	Start.lstStartsList.Items.Add("value to be added")
	Start.count += 1
	Start.values(Start.count) = "whatever you want it to be"

Note that any form in the program should be able to do this.   Questions/Comments/Wonderings?

From a "calling/parent form" to its "child"

For other kinds of sharing the situation is a little different. Our programs will typically have the start form create or call other forms via ???.ShowDialog() or ???.Show(). The "calling" (or "creating" or "parent") form creates the form and can set values for its controls and public class variables. The direction of the sharing is forward rather than back as with the start form discussed above. Typically, the values will be set before the new or child form is "shown", i.e.,

	Dim nextForm as formName
	nextForm = New formName
	nextForm.publicVariable = "data placed in the next form's public string variable"
	nextForm.lblHeading1.Text = "Text For First Heading"
	
	nextForm.ShowDialog()

(The values in italics are made up names for variables and controls.) Again, a calling form can set or change the values of any public variables and control properties in the form about to be shown before it is shown.

One could use tis technique to pass data from one calling form to the next and the next calling form to the one after it and so on and so on and so on ...

Questions/Comments/Wonderings?

From a "child" form to its "caller/parent"

Occasionally, a form will want to pass data to the form that called it. If the calling form was the start form there is no problem. In other cases, however, it is not so straightforward.

Object oriented languages have the notion of parent or caller, but Visual Basic seems not to implement it in a way that is directly available to child/called form. The solution is to have the caller/parent form tell the child/called form who its caller/parent is. The programmer must realize the need for this kind of communication and plan for it by creating a public variable in the child/called form that is the same type as the caller/parent form. Then, ...

Assuming we have forms start, first, and second and that first has a listbox called lstOrder, the following code could allow code in appear second to change the listbox in first.

This process allows you to get data back to the calling/parent from from its called/child form.Questions/Comments/Wonderings?

Sample Code

The file DataSharing.zip in the notes folder contains a VB project that you can run to see all these data sharing techniques. It can be a bit confusing, so feel free to ask questions about anything that is fuzzy.

Next Time

Day 23 — Unit VI (Additional Topics)

Generating Controls on a Form

Logistics

Project

If you have not yet done so, prepare your project proposal and schedule a meeting with me.

Those who have submitted a proposal and met with be, be working on the project and preparing for the project check-in meeting. That should happen in the next 2-3 weeks or as soon as you have completed enough of the work (see the description).  Any comments, questions, wonderings?

Generating Controls on a Form

We examined a partially completed program for playing Yahtzee. (You can download and unzip the program to see the various code examples.) Some points about using code to build forms rather than doing so by hand are:

Next Time

Day 24

Instructor Gone—Project Work (on your own)

I will not be in class. We decided that you would work on your own on your projects.

Next Time