CISC181(S21) Lab 3
This lab requires that you download the file Lab03.zip from onQ. Don't be dismayed by the length of the lab: The text is mostly explanatory. You will have to pay attention to what you're doing, however.
Submission guidelines
The problems on this lab require a mix of solution types. Part 1 requires you to type a solution into text boxes on this document and to edit an HTML file. Part 2 requires you to copy and edit another HTML file. Place the following into a zip file called Lab03_solutions.zip:
· Your completed Lab03.docx file
· Your completed beancounter.html file
· Your completed binary_search.html file
Upload that .zip file to onQ by the lab's due date and time.
Part 1 (10 marks)
3. (1 mark) When a variable is declared, it may also be initialized (given its initial – meaning first – value) using the assignment operator (an equals sign). What is the initial value of cupSize?
4. (2 marks) Nationalist sentiments being what they are, Head Office wants us to "Canadianize" our product line, and of course, our Canadian sales taxes are different from the American taxes built into the program currently, so that needs to be changed, too. As you make each change, test it in your browser. For both marks, your program must run correctly after making these changes. Here are the changes you are to make:
a) The drink known as Americano is to become Canadiano. (Note that there are nine occurrences of Americano, spelled with upper- and/or lower-case letters, forming parts of identifiers in some cases, in the program and the form. Make sure you change them all, preserving the case, upper and/or lower, of the characters as you go.) Your editor likely has a search-and-replace feature that lets you specify whether you want to consider character case in your search. Make sure you select that option. Getting the character case incorrect may mean the difference between your program working and not working.
b) There is a variable called taxRate that is currently being initialized to 0.0848 (which is the tax rate for some American state). This value should be changed to 0.13, representing Ontario's 13% Harmonized Sales Tax (HST). (Note that Ontario charges a reduced tax rate for total restaurant orders of $4.00 or less, but we will not worry about that.)
5. This is the trickiest part of the lab. For full marks, your program must run correctly after making these changes. Try to preserve the indenting patterns used in the original code.
You are going to introduce a new control on the interface and change the program logic to work with the information it provides. As with all the foregoing, make sure everything is typed exactly as shown, paying special attention to the names of elements like "staffDiscount" with mixed upper- and lowercase letters.
Here is the idea: Head office has decided to offer employees a discount on purchases. It is to be applied to the subtotal amount when the user clicks on the "Total" button and will show up as a separate line item in the "Amounts Owing" box as "Staff Disc". The system user indicates whether a staff discount is to apply to a purchase by means of a checkbox control placed to the right of the "Enter" button. If this control is checked…
… then the discount will be applied when the user clicks on "Total." If it is left unchecked (the default state)…
… then the discount will not be applied.
I have already made some adjustments to the styling of the web page to accommodate the new control and its output field. Here is what you need to do to incorporate them and make them work:
a) (1 mark) We'll make changes to the interface first, starting with the checkbox and its label. Find this line down in the <body>…</body> section of the HMTL:
<input type="button" id="enterItem" value="Enter">
Insert these four lines immediately below it:
<label for="staffDiscountCheckbox">
<input type="checkbox" id="staffDiscountCheckbox">
Staff discount
</label>
Try to preserve the indenting at that point in the file. Save your work and preview it to see that the checkbox and its "Staff discount" label appear as in the second image above. (You should be able to check and uncheck it at this time, but it will not affect calculations yet.)
b) (1 mark) Now we will add the output box for the staff discount amount in the "Amounts Owing" area. Find this HTML:
<label for="subtotal">Subtotal</label>
and insert the following lines below it:
<input class="number" type="text" id="staffDiscount" value="$0.00" readonly>
<label for="staffDiscount">Staff Disc</label>
Save your work and look at it in your browser to see that the "Staff Disc" label and its field appear under the "Subtotal" field in the "Amounts Owing" section of the interface. At this stage, the interface is complete, but the new additions still do not affect the operation of the program because you have not done any actual programming with them. That comes next.
c) (1 mark) Head office has said that it wants to experiment with the concept by first giving a 6% discount to staff. As this might change in future, it makes sense for you to introduce a variable that is declared and initialized near the top of the program, perhaps where a great many other variables are declared and initialized. So, find where the variable called price is declared and initialized to 0.00 up in the JavaScript. code, and insert a declaration and initialization for a new variable, staffDiscountRate, below it as follows:
var staffDiscountRate = 0.06;
Save your work.
d) (2 marks) When the user clicks on the "Total" button, the function called total() is executed, causing calculations to happen and numbers to appear in the "Tax" and "Total" fields. This is where we are going to add the logic to test whether the user has "checked" the "Staff discount" checkbox, and, if so, make the amount showing beside "Staff Disc" change from $0.00 to whatever the calculated discount should be, and then to have this amount be used in the "Tax" and "Total" calculations. First, find the code for a function called calculateTotal(), then replace that code with this,
function calculateTotal () {
var staffDiscount = 0.00;
if (document.getElementById("staffDiscountCheckbox").checked) {
staffDiscount = Math.round(subtl * staffDiscountRate * 100)/100;
}
document.getElementById("staffDiscount").value = "$" + formatPrice(staffDiscount);
var taxes = Math.round((subtl - staffDiscount) * taxRate * 100)/100;
document.getElementById("tax").value = "$" + formatPrice(taxes);
document.getElementById("amountOwing").value =
"$" + formatPrice(subtl - staffDiscount + taxes);
}
Having made these changes, save your work, then refresh your browser window and find out the cost, tax included, of 1 espresso, 2 short lattes with three shots of espresso, 3 tall cappuccinos, and 3 grande Canadianos, first without a staff discount, and then with?
As was the case with Part 1, this portion of the lab will again have you making some changes to a JavaScript. program using a program editor such as Visual Studio Code. This time, however, you are going to be working from a pseudocode algorithm that you have seen before, that is, the one presented in the Algorithms module for binary search, to implement the algorithm in JavaScript. so that it works on real data.
To begin:
· Extract the linear_search.html file from Lab03Files.zip to a convenient place for working on it, like your computer's desktop.
· Open linear_search.html in your editor.
Here is the algorithm for binary search in pseudocode. You will be referring to it a lot while you gradually turn it into JavaScript. code that can be compiled and executed in your browser.
let n be the length of the list of numbers
let a be the list of numbers (a0, a1, a2, ..., an-1)
let val be number being searched for
let low be 0
let high be n - 1
while low ≤ high
let mid be floor((low + high)/2)
if amid = val
report position of val as mid
stop
if amid < val
let low be mid + 1
otherwise
let high be mid - 1
report val is not in the list
Something to keep in mind: The indenting in the pseudocode, above, is significant. It shows us which statements belong to the while loop, and which belong to the two if statements and the otherwise statements. Importantly, it tells us that the report statement is not in the while loop. Your final JavaScript. code should retain this indenting structure. It will help you (and a TA!) to spot potential problems in your code.
Since there are two levels of indenting in this pseudocode, your finished code should contain corresponding JavaScript. blocks, each of which begins with a "{" curly bracket and ends with a "}" curly bracket. To make it more obvious where the blocks are, I have expanded my pseudocode below to include "{" and "}" block markers and made them bold so they are easier to see…
let n be the length of the list of numbers
let a be the list of numbers (a0, a1, a2, ..., an-1)
let val be number being searched for
let low be 0
let high be n - 1
while low ≤ high {
let mid be floor((low + high)/2)
if amid = val {
report position of val as mid
stop
}
if amid < val {
let low be mid + 1
}
otherwise {
let high be mid – 1
}
}
report val is not in the list
Refer to the above pseudocode frequently during this lab, as it accurately reflects the indenting and correct placement of the curly brackets in the JavaScript. program you are about to create.
We're going to start with a big advantage: An existing program that demonstrates a linear search. It's in the Lab03.zip file, stored as linear_search.html, so extract it, open it in your text editor, and have a look at it there or on the next page.
<!DOCTYPE html>
<html>
<head>
<title>Linear Search demo</title>
<script>
// Test lists
var word_list = ["cat", "duck", "frog", "rat", "unicorn"];
var number_list = [-12, -7, 0, 4, 5, 10];
function find(a, val) {
var n = a.length, i = 0;
while (i < n && a[i] != val) {
i = i + 1;
}
return i;
}
function report_result(a, val) {
var pos = find(a, val);
var list = a.join(", ");
if (pos < a.length) {
alert(val + " is at position " + pos + " in " + list + ".");
} else {
alert(val + " is not in " + list + ".");
}
}
// Test code
report_result (word_list, "cat"); // found
report_result (word_list, "cow"); // not found
report_result (word_list, "rat"); // found
report_result (number_list, 0); // found
report_result (number_list, 10); // found
report_result (number_list, -11); // not found
</script>
</head>
<body>
</body>
</html>
The first thing you should notice about this file is that it is a lot shorter than the beancounter.html file from Part 1. Yay!
Drag the linear_search.html file from a file listing onto an open browser so you can see how it looks when it is working. It just produces a series of pop-up messages showing where test values were (or were not) found in the test arrays.
From the code, you'll see that it uses two so-called array variables, word_list, and number_list, to test its six-line find function (the code in the middle). You can see other array variables being used in an example in the Programming 2 slides. The object of this exercise is to change the code in the find function so instead of performing a linear search, it performs a binary search.
From the Algorithm module's slides, you will learn that for a binary search to work, the data it is searching through must already be sorted. Happily, the six strings in the word_list array and the six values in the number_list array are already sorted! (If they weren't, we could apply the exchange sort algorithm to them to get them sorted.)
Save the file under the new name, binary_search.html. Now to make a purely cosmetic change.
· Change the contents of the <title> tags from "Linear Search demo" to "Binary Search demo". (This changes what appears in your web browser's tab.)
Before we start messing with the actual code, I'll explain a very little bit about how the find function works. It does a linear search, using a variable called i (for "index") to keep track of which element of the array called a (just like in the pseudocode!) is being compared with the search value (called val) during a specific execution of the while loop. i has an initial value of 0, because array positions start at 0 in JavaScript, as mentioned in the slides. The while loop will exit for one of two reasons:
· the value of i is no longer less than the length of the array, in which case the linear search has not found value val in a, or
· value val has been matched to an element of array a, in which case the value of i is the position of value val in a.
So, what does that mean? It means that if variable i ends up with a value less than the length of the list being searched (a), the search value, val was, indeed found in the list. Otherwise (the value of i reached the length of the list being searched), the value val is not one of the elements of the list a and the search failed. The rest of the program learns of this by way of the statement
return i;
in the find function. return causes the function to end and for any value that follows the word "return" to be given back to whichever statement called the find function. In this test program, that happens to be the statement
var pos = find(a, val);
in the report_result function. (I chose the variable name pos because, as you may have suspected, it is short for "position", as in the position at which value val was found.)
Okay, this all means that if we're going to use the same test code when we create our binary search version of find, we need to make sure it returns either the position (or index) of the search value in the list/array being considered, or the length of the list if the search value isn't in the list.
Back to the code:
· Delete all the statements in the block of the find function but leave its opening and closing curly brackets in place.
function find(a, val) {
}
Everything else you must do for this lab will go on new lines between those curly brackets.
Now let's look back at that pseudocode. It starts off by creating a bunch of what look like variables, two of which (conveniently!) have the same names we've been using, a to represent the array or list containing the various sorted values, and val representing the value we want the function to search for. Now, the Programming 1 slides talk about information being conveyed to functions by way of arguments, which are the things that go inside the round brackets in a function call like
report_result (word_list, "cat");
Inside the function itself, the values of the arguments become the values of the corresponding parameters, which is what the a and val are in
function find(a, val) {
So that means, we already have our a and our val from the pseudocode and don't need to recreate them with variable declarations. We DO need to do that, however, for the other variable-like things in the pseudocode, namely n, low, high, and – when we get to it – mid.
Start with n. It's needs to be initialized (have its first value set) to the length of a. You can see from the original code that there are a couple of references to a.length. Length is a property of arrays in JavaScript, and a.length computes to the length of a. That means your line declaring n, the first line inside those curly brackets, should be
var n = a.length;
(and yes, I know, we already had a statement that contained that, and I told you to erase it. What can I say? Sometimes it pays to start over.)
All right, so that's how you declare a variable. Now,
· do the same for low and high, letting the pseudocode guide you as to what their initial values should be. Remember the var keyword, and the semicolons (;) statement terminators at the end of each line.
You should be ready to write your while loop. For guidance to the syntax, look at the while loop in the original code. It has a reasonably elaborate Boolean expression in its round brackets. Your replacement will be much simpler. Note that the "less than or equal to" operator you'll need is this: <=, a less than sign immediately followed by an equals sign. Remember the while loop's opening and closing curly brackets! All right,
· create the first and last lines of your while loop with its opening and closing curly brackets (which you'll fill in later). Remember to put your Boolean expression in round brackets.
Notice from the pseudocode that all the statements in it are indented at least one level. Make sure yours match! It just makes the code tidier and easier to understand.
Inside your while loop:
· Declare and initialize the mid variable. For this, you'll need to know how to do a floor operation in JavaScript. (If you're not sure what floor functions do, Google it!) It happens that JavaScript. has a library of mathematics functions called Math, and one of those functions is called floor! Here's how you'd get the floor you're looking for:
Math.floor((low + high) / 2)
(Note the round brackets around the low + high. They're important!) So, when you are declaring your mid variable, initialize it with the expression I've given you above.
Next, the pseudocode has an if compound (multi-line) statement. JavaScript. has one of those, too, of course, and its syntax can be seen in the report_result function in the original code. The pseudocode doesn't call for an else part as is used in report_result, so you can ignore that for now. Anyway, continuing inside the while loop:
· Create the first and last lines of the first if statement with its opening and closing curly brackets. Like while, if requires a Boolean expression that must be enclosed in round brackets. To compare two values for equality in JavaScript, you don't use an equals sign, you use two of them together like this:
==
To write
amid
in JavaScript, you'd use this array notation
a[mid]
resulting in the complete Boolean expression:
a[mid] == val
Now we go inside the curly brackets of the if, indenting the contents another level as indicated in the pseudocode.
· The two statement in the pseudocode,
report position of val as mid
stop
get replaced with just one: a return of mid. See the syntax of the return statement in the original linear search version of the find function.
Okay, that's the end of the first if statement, so move past its closing curly bracket and onto the second if statement. This one uses an otherwise clause in the pseudocode, and for that in JavaScript, you use else instead as you can see in the report_result function. So:
· Create your second if and an else (in place of the pseudocode's otherwise). If you've already declared a variable, don't use the var keyword with it again, just assign a new value to it as in
low = mid + 1;
Remember that the Boolean expression of the if (the else doesn't have or need one) needs to go in round brackets and your indenting should match the pseudocode. It's your choice if you write
} else {
or
}
else {
JavaScript. doesn't care (and neither do I! Some code styling is at the programmer's discretion).
Nearly there, folks! All that's missing is a return statement, after the closing while curly bracket but BEFORE the closing function curly bracket. This is the return that needs to indicate to the rest of the program that val does not occur in the array, so the value returned should be, if you recall, the length of the array, a. And somewhere back there a couple of pages is that information. So:
· Write the last statement of your find function. It returns the length of array a.
Save your work, then refresh your browser window to see if your program works. If it doesn't, you may have to close your browser window and open it again. Before you try running it a second time, check your code against the pseudocode first to see if you've missed anything. In my own attempt, I ended up with a find function that was 17 lines long, top to bottom. Depending how you write your else, you may end up with 18 lines instead, but it shouldn't be longer than that unless you've got some blank lines. The 15 marks Part 2 of this lab is worth corresponds to each of the 15 essential lines of code inside your find function's opening and closing curly brackets. Good luck!