Wall 1

Let's Stack Some Bricks

Overview

This program is not likely to make you a Google-style billionaire, but it will serve to ramp up your practice from the small programs we have been writing in class. The computation we will perform doesn't even have a cool name like "The Sieve of Eratostheses" or "Fast QuickSort". It's just an excuse to glue some loops and conditionals together and to use some of the patterns we are learning to produce a cohesive larger program. So we will just call it "Stacking Some Bricks".

In this program you will combine the basic structures and patterns you have learned so far into a larger computation:

That's a pile of stuff and you probably didn't even realize how much you have done until you saw it all in a list.

This program is also an opportunity to learn and practice a few new concepts. One is input data validation. You will be doing input data validation from now on in all your program assignments. Another is the process of programming. You will follow some disciplined and systematic construction practices that will make creating a correct program much easier than if you just went at it with no guidance or plan.


Input

The user will first input a number "num", a positive integer. Then the user will then input "num" triples of numbers; these can be any real values.

For example, if the user first gives the integer "2" the program will then two times ask for 3 numbers (I am calling it a triple but you will use 3 separate prompts to get them). The user might give 126, 4, and 76.3 as the first triple. Then the program does it again to get the second set of 3 numbers; the user might give -3.4, -18, and 543.21 as the second set of 3 numbers.

You must validate the input values given to the program (see below). This means you must be sure the user gives input values that match what the program expects to be given.


Output

Your program will print these results in the given order:

  1. average of the maximums from each triple
  2. largest of the minimums from the triples (I'll call it bigMin)
  3. the largest individual value over all numbers (I'll call it bigMax)
  4. the number of the triple bigMax was in
  5. the count of how many inputs were negative
Truncate all numbers that are not integer to 3 decimal places.

For example, let's assume we got the input above from the user:

  num    triple 1         triple 2
  2      126, 4, 76.3     -3.4, -18, 543.21 
The program prints 334.605 as the average of maximums, from ( 126 + 543.21 ) / 2
The program prints 4 as the largest of the minimums (larger of 4 and -18)
The program prints 543.21 as the largest of all the values
The program prints 2 as the triple containing the largest value
The program prints 2 as the count of how many values are negative


Input Data Validation

Users make mistakes when typing input data. Your programs should be written to do something useful when that happens. Grinding to a halt and saying "urp..." is not particularly useful; neither is taking bad input data and just computing anyway, generating nutty and wrong results. Input validation is one aspect of making a program what we call "robust". A robust program resists crashing and works rationally in the face of errors or unexpected events.

First thing we need to do is detect when the input data is invalid. There are several things that can go wrong:

You will use conditional statements (if-then-else) to decide if the input values you get from a prompt statement meet all the requirements. If an input value fails to meet some requirement, the program must take some appropriate and useful action:


Test Data

Run your program on these test cases. Make sure your program produces the output indicated. Note the error cases at the end.

--------------------------------------------------------------

input:  num   triples             
        3     (2,4.5,1) (300,7.1,21.3) (11,2,5)
output: avg max  bigMin   bigMax   triple    #negatives
        105.166  7.1      300      2         0

explanation:
            t1    t2    t3    
    maxs    4.5   300   11    105.166 is the average of these
    mins    1     7.1   2     7.1 is the largest of these
    bigMax  300 is in t2      it is the largest of the maxes 
                              (and so the largest of all the numbers)
    #negs   0                 as none are given        
                 

--------------------------------------------------------------

input:  num   triples             
        4     (-17,19.1,3.2) (1,-4,2) (8.3,21,101.02) (18.4,17.7,-13)
output: avg max   bigMin    bigMax    triple    #negatives
        35.13     8.3       101.02    3         3

--------------------------------------------------------------

input:  num   triples             
        2     (11.3,-14,-1) (-100, 3.321, -12.2)
output: avg max   bigMin    bigMax    triple   #negatives
        7.31      -14       11.3      1        4

--------------------------------------------------------------

input:  num   
        -3
output: error

--------------------------------------------------------------

input:  num   
        0
output: error

--------------------------------------------------------------

input:  num   
        6.5
output: error

--------------------------------------------------------------

input:  num   
        tarheels
output: error
        
--------------------------------------------------------------

input:  num   triples  
        2     ( 2, 3, four ) 
output: error
        
--------------------------------------------------------------

Design Considerations

Think "for loop" for this program (since it's the only loop we know). The body of the loop will execute "num" times, where "num" is that first integer you get from the user. Each time through the loop, the body will do these things:

In the loop body, each time you get a new set of 3 numbers you will have to identify the largest and the smallest. To do this you can use conditional statements individually, or you may find Math.max and Math.min useful. You can google up their details, but basically they work like this:

   Math.max(5,9) evaluates to 9
   Math.min(3,7) evaluates to 3
   Math.max(x,y) evaluates to the smaller of the values stored in 
                 the variables x and y

No real surprises there. Well, one surprise. It might help to know you can give Math.min and Math.max as many arguments are you want... for example

   Math.max(6,2,8,3,19,11,4) evaluates to 19

Fun with min and max

Just for fun, note you can also find the max (min) of 3 numbers with nested function calls, this way:

This first finds the larger of b and c, then takes that result and compares it to a. So the overall largest wins. This also extends to more than 3 values:

   Math.max( Math.max(a,b), Math.max(c,d) )      4 numbers, 
      will work for any number of arguments that is a power of 2

   Math.max( a, Math.max( b, Math.max(c,d) ) )   4 numbers
   Math.max( a, Math.max( b, Math.max( c, Math.max(d,e) ) ) )   5 numbers

    etc...
Here is an alternate formatting for 5 numbers, to help you see how an inner function call produces a value to be used as an argument for an outer function call. Remember, when function calls are "nested" like this, the parentheses tell the JavaScript interpreter to do the most deeply nested (inner) calls first, then work outward. You can think of the Math function call "turning into" the value it computes as a result:
   Math.max( a, 
             Math.max( b, 
                       Math.max( c, 
                                 Math.max(d,e) 
                               ) 
                     ) 
           )   

This is all for your personal edification. These approaches work when your language has a Math.max that only takes 2 arguments. It also helps you better understand how to call functions like this and how to pass arguments for them to work on.

Try all this stuff in Bricks or JSFiddle, see how it works, experiment and learn. There is a whole world of programming fun out there for you to just reach out and grab onto !


Notes on the Process of Programming

Although we often refer to the act of programming as "writing a program" it is not really like writing in the "term paper" or "blog entry" sense. You will not sit down, then type, type, type until you have a page or two, and then hold your nose and hope the compiler gives you a "like" when you try to execute it.

"Writing" a program is more like constructing a house. You should follow a step-by-step procedure and build your program small piece by small piece:

No analogy is perfect; I won't try to map your activities onto plumbers or carpet installers. However, there is good similarity here to the disciplined and systematic step-wise approach you should take in building your programs.

The primary principle to remember and follow is this:

Never write a big pile of code and hope for the best when you run it. It will have errors, and it will be difficult to determine which of the dozens of lines of code is the problem (or problems). Your "debugging" task will be time consuming and confusing. Instead, you take a working program, add 5 lines of code, and test it. If you find an error, then the error is most likely in one of the few lines you added.

This bring up the next principle:

When I say "working version", I mean a program that is syntactically correct, compiles and runs, and produces the output that you expect it to produce. It won't be doing everything the final version should do, but it is doing what you think it ought to be doing at this incomplete point. Let's say you have a working version, and you add a few lines of code... and it is suddenly hopelessly broken.... well, you can always just go back to that previous working version and "start over" without losing much time or wasting much effort. Just take out the few lines you added, rethink things, keep calm and carry on.

Begin construction with a basic (and simple) working version. It might do nothing but print "ok its working" but at least it is doing what you expect it to be doing. In keeping with our construction analogy we can call this the foundation. Then your grow your program towards the final functionality, adding one small piece of behavior at a time.

The next steps we can think of as "framing it up". Your new working version does not have to be much more than comments and alert statements. Identify the major chunks of activity the program must have and make a comment in place of each. Put in a few alert statements that announce something like "now doing the main processing loop" or "getting an integer from the user" and later you will replace those with real working code that make that activity actually happen. When you run this skeleton it will announce things in the right order and stop. It is not doing what the final version should do, but it is doing what you expect so it is a working verion.

Save that working version (so you can revert to it if the world goes all pear shaped), and now go on to make successive new working versions by adding the sheathing, roofing, windows, etc. one small chunk at a time. Each time you add a new chunk you have to make sure it is doing what you expect it to do. Then save that new working version and start another. Lather, rinse, repeat.

Each time you add a little code to build a new version you have the task of showing that it does what you expect it to do. And so, this brings up our next principle:

In building a house we test the plumbing to see if it holds pressure before we bury the pipes in the drywall. In the same way, we test the code we write before we "bury" it in more code. Testing means thinking of all the different kinds of input the code could get and seeing if it responds the proper way to each kind. You should use lots of test cases, not just one or two. Try to work at the extremes of the ranges of values the data can have. Then work beyond the extremes...see if your code can handle the bad data robustly.

Does the code expect integers from 1 to 100? Then try 1, and 100, and 2 and 99... and then 0, -1, 101, ... then some in the middle of the range. Then try some numbers that are not integers. Then try some input that is not even numeric.

I am using "input" as a generic term here. As you add code to your program you will often be creating "fake" input to test parts that are not directly receiving values from the user. Eventually you will grow the program to the point where it is using user-supplied values.

Be liberal in your use of alerts to see what variable values are when you are trying to figure out if your new code is working correctly. You can always take them out (or comment them out) when you are happy with what the code is computing.

Divide and Conquer

Once you have the skeleton framed up, you have to decide what to work on first to flesh it out. There is no one way to do this... you might want to work on the easy parts first and get something working quickly as an encouragement to keep on going. Overall, this approach is called "divide and conquer". Rather than see the program as one big problem to solve, we make progress by breaking it into a bunch of smaller problems. We solve the smaller problems one by one and compose those small solutions into a big problem solution. For this program, here are some considerations.

Validation first? Last?

One common thing to do is work on the functionality assuming the input values are good (you assume that when you test you are able to type in correct input values... we will not make that assumption for the general user however). Once the program is producing correct output for correct input, you then go back and add code to do input validation.

Others prefer to do the input validation code first. The "functionality" of the program would consist of nothing other that echoing the input data to the output, or printing error messages if the input is bad. Once the program properly passes on (prints out) good inputs and weeds out bad ones, you can go in and add code to compute real results with the good inputs.

Loop body with partial functionality

For this program you will be doing several different things inside the loop body with each triple. You will be finding max and min. You will be accumulating max's for the final average. You will be checking min to see if its a new bigMin. You will be counting negative inputs... etc. One thing you can do to build in small pieces is not work on all those things at once. Try building a program that does nothing but take the user input triples and count the negative ones. After the loop completes print out that count.

Now you have a working version that does one of the many things you are supposed to compute, and a loop is part of that structure. Go back and create the next working version by adding to the loop body the parts that find max's, and compute the average after the loop. Then add another function... and so on.


Submitting your Work

When your program is working (it gives the right results for all your test cases, and for mine above) put your source code in a single text file on your computer with extension ".js" (for example, "program1.js"). Upload this program in the Assignment 1 area using the class Sakai pages. We will return your grades via Sakai as well, with any comments explaining what need to work on for the next one.