Oftentimes you will want to gather input from a user and you'll give the user instructions such as, please enter a value less than 100. The user then may do something wrong, as users sometimes do. So, here we have someone disregarding the instruction and now what do you do? often times what you do is you simply, politely ask again and the user might still get it wrong not understanding that less than 100 means less than 100. So, you'd ask again. And now finally, the user gets it right. This is a very common situation, and it turns out to be surprisingly difficult to program. Here are two possible solutions to this, the blue one and the black one. And, I'd like you to look at both of them and see which one would work. Take your time, and tell me, is the blue one the right one? The black one, do both work? Or does neither of them work?
Well, they're actually both right, let's see why. Let's look at the blue code first. When we start, the value is 100. We go to the loop, value is greater or equal than 100. In fact, we've set it, to make sure we enter the loop. We ask the user to enter a value. Let's say the user is uncooperative and enters 200, then we go back up. Now, the value is still greater or equal than 100. And we go back in the loop. That was the whole purpose really, of this while loop. We wanted to keep asking the user while the value is greater equal than 100, since our target is to get a value less than 100. Remember, the while condition is always the opposite of the target. So now, let's say the user is doing better, enters 99. I'll go back to the top of the loop. 99 is less than 100. And we follow through, so it worked. So this one was a good solution. Now, let's look at the black solution. This one is a little different, we ask the user to enter a value less than 100 and let's say they do, then now comes the loop. This loop is never entered. And in this case we get the right behavior. So, let's look at another situation where the user doesn't makes a mistake first. So, we're again at the top. We ask the user to enter a value less than 100. The user enters we ask the user again and say no if they give the right answer. Then we go back to the top and now we're satisfied, so this also works. But both of the solutions are a little unsatisfactory. Look at the first one here. We have this trick where we're setting the value to an artificial value not to a user input so that we enter the loop the first time, it's a bit ugly. The second one we repeat part of the code. Look at this statement here and the statements here, they're exactly the same statements and we need to repeat them because we first need to get the user input before we can see whether it's any good. And then, we need to keep bugging the user until it's any good. That repetition is also, somewhat undesirable. There is a Java statement that can take care of this issue.
So, here is this pesky loop that didn't quite work out for us. We want to keep asking the user to enter a value less than 100 and we want to keep doing it while they enter a value greater or equal to 100. But really, what we want to do is we want to go through the loop once. And then, do the check, and then if the check fails go through the loop again and so on. The key is we want to go through this at least once, the way to do that in Java is to move the while statement, the while condition to the bottom and to add the word do to the top. If there were two on the top, the Y condition on the bottom and this is called a do loop. Let's look at a program that uses it. So, here's our program. It has the do loop that you've just seen. So, we keep asking the user, please enter an integer, less than 100, until they do. In other words, we stay in the loop while the input is greater or equal to 100, and then this program doesn't do anything with it. It just says thank you for entering a value less than 100. Let's, let's run this program so you can see it in action. The program has started, it asks for an integer less than 100. We'll be annoying and put in forever, or not. And now, the program simply says thank you for entering 90. That's the most common reason really to use the do loop for input validation. And the reason you need the do loop is simply you've got to get at least one input from the user before you know whether to complain or not. So, this is the situation where you've got to enter the loop at least once before you know what to do, that's exactly the point of the do loop. The do loop enters the loop at least once, whereas the while loop might never enter the loop.
Suppose we want to check for inputs that are at least 0, and at most 100. How would we modify this do loop to look for a number that's between 0 and 100, and could be 0 or 100?
Well, first of all, this prompt doesn't match what we want the user to do anymore. That's a bit better. We also need to check more things about the value. If it's too low or too high, we keep asking. Too low would be that value is less than zero. And too high would be if the value is greater than 100. If either of those is true, we want to keep going. Let's test this out and make sure that the edge cases work well too. It's asking me to enter an integer between zero and 100 inclusive. If I enter something like 101, it'll keep asking. That's good. Now, if I enter something like negative one, it still keeps asking. That's also good. But it should accept zero and 100. Looks like it accepted zero. Let's try running it again. Will it accept 100? Looks like it accepted it. So this was the correct condition to check the user's input.
In lots of applications, you need to read many inputs. Here, when you look at these adorable kittens, of course, you want to pet them all. But if you're a vet, you might also need to know their weight, their average weight, which one's the largest, which one's the smallest, and so on. So, a program that processes data like that needs to read in all of that information. It needs to specifically, in this case, read in the weight of every kitten. So, we might want to prompt to enter the first weight, the user enters first kitten weighs the third weight and so on. Well, when do we stop? Maybe we could have asked the user how many values are you going to enter? Now, that's a little clumsy, particularly when there's lots of values. There's a common solution to this problem. And that is to ask the user to enter some input, that definitely can't be the right value. In this case here, we might ask the user to enter 0, because no kitten can weigh 0 grams. Such a value is called a sentinel value.
Why sentinel value? Well, a sentinel is like the fellow you see over here who guards something. In our case the sentinel is that last input that guards the input sequence. It just says this is the end of the input sequence. Do we have to use zero as a sentinel? No, there are other possibilities. For example, minus 1 would work if all inputs must be greater or equal 0. Or if any numbers can be inputs, you could use the letter Q. In the exercises that come, you'll explore some of these possibilities.
Let's practice collecting multiple values. I want to write a program that helps a user compute their average time for sprinting 100 meters. Here are the beginnings of the average method. The method should be reading floating point numbers from the user until the user enters zero and then it should return the average of all of the numbers. You can use the average tester main method to test as you go along.
We want to figure out what the sum of all of the numbers that are entered is. And also what the count is. So, we can use them to calculate the average and return it. We can use a do loop to collect the user input. Somewhere in the do loop we'll have to update value by getting the next user input. So, we'll need to ask the user for a double, and then read in the next double using the scanner. If the value is 0, then we want to quit, but if it's not, we want to increment count and add to the sum. So, count goes up, and sum goes up. So, how long do we want to keep going? We want to keep going as long as value is not 0. Because if value is 0, that means we read a 0 from the user. So, we continue doing these steps, as long as value is not 0. Let's try this, after I fix up my syntax errors. Running the main method. If I enter 2, it doesn't quit. Negative program again though. There's some repetition here. We have to check that the value is not equal to 0 in two different places. We could change this so instead of using a do while loop it would just use a while loop and only check the value as not equal to 0 in one place. We would declare a Boolean to track whether we're done or not yet. Initially, we're not done. So, it starts as false. Then, as long as we're not done, we would keep doing the things in the loop. So, we wouldn't need the while at the bottom. As is, this loop would go forever because we never change the value of done. So, when do we want to say that we're done? Well, we want to say that we're done if somebody puts in a 0. So, if the value is 0, then we set done to true. This will work the same way. When I run the main method, it'll ask for doubles until I enter 0, and then give the average. You can structure any loop and a half this way using a Boolean to guard your condition.
A lot of the time, you can't exclude zero. I'm writing a program that calculates the average altitude of an area. Since some parts are below sea level, negative altitudes are allowed. And an altitude of zero would be allowed too. Can you modify the average method so that the sentinel is a letter Q instead of a zero? It's possible to do this without a loop-and-a-half. If you're feeling adventurous, you can try Googling Java 7 Scanner, and finding the documentation about the hasNextDouble method. I'll talk about this in the answer video.
Let's see how we would do this in BlueJ. Here's the loop that we had before, which used zero to decide when to quit, but now we want to go as long as there's another double. We can use the hasNextDouble method to ask the scanner if the next token it has is a double. Then we basically want to do the same stuff as before, right, asking for input and adding it to the sum in counting. Let's just try this. Hm, still running, still running. Okay, I think there's a bug here. So, let's stop this. Look at the code again. Where could this have got stuck? I've never seen simple variable assignments like this get stuck before. So, I think that's not the problem. Now, here I check if there's another double. Oh, there isn't another double, because I haven't asked for one yet. So, I'll need to ask the user for a double before I check if there is another double. And now, I'll have to ask again somewhere in the loop. I don't want to ask immediately because then I'll ask twice. So, I probably want to ask at the end, after I process the value I already asked for. Let's test this again. I can enter three, and four, and zero. Well, it looks like this message isn't right yet. But, if I add a q then I get my average back. That's interesting. This is the average of only the first two numbers, it didn't include the zero, I guess we're still not there yet. We're only incrementing the count if the value is not zero. [LAUGH] We've also got this cruft with the done, we're not going to need that anymore. So, I'll take out everything associated with done. Now, let's see. We have the user enter something If it's a number, we read it in, add it to the sum, count, and ask for another one. This is probably what we wanted. Now, if I enter three, four, and zero, that's better.
Now here's a question. What does the average program print if you immediately type Q when prompted for a value? You may to try this out in BlueJ. Does the program print zero? Does the program terminate with an arithmetic exception? Does it print NaN? Or does it do something else?
Lets try it out. Here is our average program. If we just enter q immediately then it tells us NaN but what does NaN mean? Lets ask everybody on the Internet. Lets search for java NaN. Looks like other people have had this question before and under the answers it says NaN stands for not a number. It's produced if some floating point operation has some input parameters that cause the operation to produce some undefined result. For example, 0 divided by 0 is arethmatically undefined. So we must have done some kind of math that was undefined. It seems like the most complicated piece of math we're doing here is just this division. So let's print out what sum and count are. Here's a print line statement from sum and count. And now, if I enter q immediately, it tells me that sum and count are both 0. So it makes sense that I'm getting NaN; I'm dividing by 0.
So the average program is in better shape than it was at first. It can now take averages that include zero, but it still chokes when the user doesn't enter enough numbers. What we would really want would be to print out an error if the user enters no numbers and count to zero. So, when we run our program, it would ask us to enter a value, and if we enter a bad value like s, it would say error, no input. But if we enter values like we did before, it'll calculate the average. Fix the average class, so that it prints out a sensible error, if count ends up at zero.
We want the code to do the same thing that it did before, but now if there isn't a next double the first time, we want to print an error instead of collecting values and calculating the sum and the average. So I'll ask the user to enter a value and then if n has another double, we'll go into the while loop and eventually calculate the average. So I'll indent all of this to put it inside of this clause. Now if they don't enter a double, I'll catch that with an else clause, I'll print out an error message. Let's check out whether this worked or not. If I enter a bad value, I get the message I wanted, and if I enter good values, then it calculates the average.
A very common problem is to find the largest element in a sequence of inputs. Now you might think, how hard can this be? In this sequence, clearly this one is the largest. But a computer program can't scan all of the inputs at once like we can. The computer sees items one at a time. So, let's look at this problem a little differently. So here I have these playing cards. I get to see one at a time. And I need to find out which one has the highest value. Let's look at the first one. That clearly is the largest one I've seen so far. The next one, well, that one is larger. The next one is even larger. This one here, not so much, so I won't change my mind about which one is the largest. That one is larger yet. This one isn't as large as the one over here,so we will ignore it and my last one here is also smaller ,so my answer is that 8 was the largest input. In Sudoku the algorithm would look like this, I start at letting the largest be the first value while there are more values I read the next value. And if that value is larger than what I thought the largest was, I changed my mind. When I'm done with this loop, largest would have been set to the very largest of all the inputs. Now you get to try this out with Java code, go ahead and complete the next project.
Let me show you what I did. I simply followed the pseudo-code that we developed before. The first line already matches. Now I have to know whether there are more values. Well, the value'=s already in this doubles. And there's a queue at the end. So I'll simply call the has next double method. Next I need to read another input. I do that here. Then if the input is larger than the largest value seen so far, I need to update the largest value. That's here and here. Let's give it a try. Here's my prompt. I'll just enter the values that we had with the cards. Here are the inputs and the Q to terminate, and I'm told the largest value is eight.
You have just seen how to write a program that determines the largest of a number of input values, but that program had an annoying flaw. The user had to type in all of the values and if there are a lot of them that can get really tedious. What we really want to do is read the values from a file. To read a value from a file, you can use the scanner class. But you construct the scanner with an object of a file class. And in turn you need to construct that file object by giving the name of the input file. Now, there's a bit of a catch here, when we put this code inside the main method, we need to deal with the possibility that there is no file called input.text. If that file was indeed missing, then the file constructor would terminate with what's called a file not found exception. That exception is considered so serious that we must tell Java that we are aware of it. That's done by putting this class throws FileNotFoundException behind main. We're not covering exception handling in this course, so this is just another one of those things that I'm going to have to ask you to just do. Once we're done with this, everything else is easy, now the scanner works. Every bit as the scanner you've been using quite a bit. You can simply call has next double, and then read a value by calling next double. Well, have a go at it. Modify the program that computes the largest value, so that it reads the values from a file.
We need to make this program read data from a file and compute the largest one. We make a file object for the file with name input.text. With that file object we construct a scanner. And now the rest is exactly like what we had before, because reading from a scanner that is attached to a file Is no different than using a scanner that's attached to the console. So here, here, and here we call next double, and has next double in the usual way. When you run this program, it'll tell you that it found, as its largest value, a value that is very large. Let's have a peek at the input file. That file has a lot of numbers in it that seem kind of meaningless. You'll see in the next exercise with Sara where they come from.
You just saw how to read from a file and how to find the largest value in a data set. The last program prints out the population of the most populous country, but it doesn't print the name of the most populous country. Edit the program so that it prints out the country's name as well as its population at the end. And here's a hint, what else do you need to remember? What variables should you add so that you can print the population and the country name at the end?
So we'll need to save the name of the country as well as the country's population. We'll initialize largest country name with the first country that we look at, sort of the way we initialized largest population with just the population that we looked at. Now when we find something which has a population larger than the largest population. We'll need to update the largest country name as well as the largest population. Now, when our program gets to the end, where we print things out, it'll still have access to the largest and the largest country name, but it's only printing out the population. So I'll add a line, print the country with the largest population is and the variable where we saved the largest country's name. When it asks for a file, I'll find the population file. And I get that the country with the largest population is Afghanistan at 3.0419E7. That seems pretty funny to me. I don't think of Afghanistan as a very big country. Let's look at these numbers. Afghanistan has eight digits. But Bangladesh has nine. Clearly there's something wrong with the program. It looks like it's set the largest population and the largest population name the first time, but then after that, probably didn't update it. So there must be something wrong with my loop. Oh! It's looking for a double first, but the format of the file has the name of the country first, and then something that could be read as a double. So this should actually just be, hasNext not hasNextDouble. Let's try this again. Okay. Now I'm getting that the country with the largest population is China, with this as it's population. That seems much more reasonable.
It's a really good idea to be familiar with some common algorithms for processing values in a loop. You've seen a couple of them already. You've seen how to compute the average of a sequence of inputs and you've seen how to find the largest element. There are quite a few more like that, counting matches, finding the first match, position of the first match, finding all matches. And so on, and so on. Instead of me tediously telling you something about each of them, we've prepared a fact sheet where you can find the pseudo-code and an example for easy reference. And Sara will practice a couple of those with you. The point is that you want to be familiar with what algorithms are out there, so that you can adapt them as necessary.
Here's another chance to practice your loop skills. Imagine you have a friend who's collecting data on climate change, and he has a data set of how much the water in a reservoir rises and falls each year. If the water level rises, he records a positive number, and if it falls, he records a negative number. Starting from here, write a program that prompts the user for a value until the user enters queue, and counts how many times the user enters a negative number. If you're not sure how to start, we've linked another fact sheet with some pseudo-code for loops with different purposes. You can use it as a guide.
The first thing I'll do is I'll ask the user for a value. Could be a floating point value because the water could have gone down by a fractional amount. I'll read the next double and if the value is negative, I'll increment the counter. Now, if I want the user to keep on entering more values, I'll need to tell them that. And it looks like I forgot colon space on the first one. That wouldn't have looked very pretty. Fix that in my compile errors, and it looks like I'm ready to go. If I run the program, I can enter values, and I've entered two negative values now, so if I finish, I expect to see the count as two. And if looks like the water line fell on two years. I'd probably want to fix out the spacing before I shared this. But right now I'm counting all of the negative numbers just the way I wanted to.
What if instead of finding how many times a value occurs in a user input we wanted to find out how many times a particular digit appears? For example, in this number 3 appears twice as a digit. You can start with this number class and implement the count matching digits method. Notice, digit to match is a digit, like 3, that you're looking for. And number is the number you're searching in.
If you remember how to count the digits of a number you'll remember that you'll need a temp so that we can count down the digits without messing up number. Now we're going to repeatedly divide temp by ten to count the, now we're going to repeatedly divide temp by ten to iterate through the digits from the last to the first. So the way that we'll iterate through each digit in the number is to first use mod to get the last digit and then divide by 10 so that we discard the last digit. Then when we use mod 10 again, we'll get the next last digit. So the first digit will be temp mod 10 and then we divide temp by 10 to discard the last digit. Now in here we want to be counting all of the digits that match digit to match. So we'll need a counter. That starts out at 0. And then every time we find a digit that matches digit to match the increment, the counter, I think I might have some type errors in here, because I'm mixing int and long. But let's see. Alright. I know that when I mod something by 10, the biggest number I'll get is nine. So I know that's it's safe to actually cast this after I take the mod and I will actually need to return the count. I wrote tester here to make sure my program works right. In this number I expect there to be four 9's, one 3's, and no fives. Since I fix all my syntax errors, I'll run my number tester, it looks good.
Let's return to our Alice in Wonderland example for a minute. What if I want to write a program that finds interesting words that occur in the book? A lot of the time shorter words are more common words. Maybe longer words are more interesting, and more likely to be related to the exact topic of the book. We could write a program that would help us test this. Let's write a program that finds the first word in Alice in Wonderland that's longer than nine characters.
We know that we're going to write a loop that searches for long words. And we know that it should stop if you find one. Or if you run out of words. If I just worry about running out of words at first, I might write my loop as while in.hasNext. Keep looking. But now, there is an other condition that I have to meet as well. I have to not have already found the word. So, i want to keep looping as long as there's an other word and the wrong word is not found. Now inside the loop, we need to actually get the next word. And then, if this word we just got is long, then we want to save the word for later, and also make sure to mark that we've found it, so that we can exit the loop. So far, I haven't created a variable for saving this, but if I scroll down a little bit, I can see that I want to print out long word. So, let's make a variable called long word. Now, in my if statement, I'll update long word and set found to true. Let's try. It says that the first long word in Alice In Wonderland is ADVENTURES. That sounds appropriate and I'd say that's an important word to Alice In Wonderland.
What if there are no long words? We could run this program on a text that has no long words. What would happen? Would the program print nothing? Would the program print the first long word is? Would we get an index out of bounds exception? Or would something else happen.
The answer is, the program would print, the first long word is and then nothing after it. Because longword would still be the empty string it was initialized as. We would never encounter a word with length greater than nine. So longword would never be updated.
So, we saw that the program we wrote won't work well if we never find something that matches. Let's update the first matching program so that prints out there are no long words if it doesn't find a long word. So that you can see the in action pretend that you're looking for words that are longer than 30 characters. Your code will go down here.
So, how do we know if there are no long words? Well, if there are no long words then long word will be an empty string. So, if long word dot equals the empty string. And remember, we use dot equals because strings are objects then we want to print out the message there are no long words. Otherwise, there is a long word and we print that out. Now, if I run this looking for words with length greater than 30, well, that's interesting. It looks like there is a word with more than 30 characters. It's just joined up with a whole bunch of dashes. I bet there aren't any words longer than 60 characters though. That would just be crazy, right? Alright, looks like this time we got, there are no long words.
You saw how to find the first match and also return if there are no matches. But what if you want to find out where the first match occurs? In our Alice in Wonderland example, maybe we find the first long word and then find out how far in that occurs to get some idea of how common long words really are. This is where the program left off. How would you modify it to return the position of the first long word? As well as that the long word was. Remember, the position of the first word would be zero, and the position of the second word would be one.
To do this, we'll need to add a counter. The counter will be incremented every time you look at a word, not only when we find a long word, because we want to count how many short words come before the long word. Let's try to hand trace a little bit and see if this would work. If the first word is longer than nine characters, that would mean we would want to return a position of zero. So we would go into the loop, we would find the word, we would go into the if statement because the word length would be longer than nine. We would mark the word as found. And then we would increment position to one. Okay, that's not quite right, so, maybe this we'll need to start at negative one. So that'll fix it for the case where the first word is long. What if the first word is short, and the second word is long? We'll start with position as -1, go into the while loop. There are more words, and it's not found yet. We read the word. The word is short, so we skip past the if statement and increment the position to zero. Now we go back into the loop. There are more words, and we still haven't found it. We read the second word, which is long so we go into the if statement, set longWord to be the word that we just read, and say that we found it. Then update the position which was previously zero, so now it'll be one, and then get back to the while loop. But we [UNKNOWN] found so we break out, this should work, but I forgot to print it. Alright it looks like the first long word in Alice in Wonderland is Adventures and its position is one. Let's compare this to the text. It looks like Adventures in position one and it's no wonder it would be related to the book, it's part of the title.
In the programs that you've seen so far, you've used a single loop to solve a problem. Sometimes, you need to have more than one loop, and in fact it can happen that you have one loop sitting inside another. Such loops are called nested loops. Let me give you an example. We want to write a program that draws this square of colors starting with the black one, increasing the greenness in this direction. Increasing the blueness in that direction. How can we draw all of these squares? Let's think of it a row at a time. For example, to draw this row here, I need to go from x equals 0 then to x equal whatever the width of this square is, which is 30 in our example. So the x equals 30, 60, 90 and so on. And that'll be a loop. That loop will draw on all of the squares in a particular row. To get to r rows, I need another loop. And there you have it, a loop inside a loop, nested loops. Now, the only tricky task remains. If I have row i and colon j. How do I find out the position and the color of this square. You'll get to try that out in the next exercise. Just keep in mind, that as the row index i goes 0, 1, 2, 3 and so on. You want the y position to be 0, 30, 60, want the x offset also to be 0, 30, 60 and so on. How about the colors. We want this square down here to have green 255 and blue 255. All the reds are 0. So, you want the greenness to vary from 0 to 255 in equal increments in this direction. And you want the blueness to vary from zero to 255 in equal increments in that direction. You just have to figure out the increments. Alright, go ahead and give that a try and as you do that closely look at those two nested loops that you'll be working with.
Well let's figure this out together. In this loop i traverses all rows. In this nested loop for each row j traverses all columns and we need to figure out x and y. Let's look at our picture. As i goes zero, one, two, three and so on the y position goes 30, 60, 90 and so on. So the y values are 30 times i. I should really be using this constant over here so let me fix that up and for the same reason the x values of width times j. What are our blue and green? Let's look at the picture again as I goes larger of the row index, the blueness increases. When I add 0 blueness is 0. When i is the maximum value of 15, the blueness is There's 16 rows, numbered 0 to 15. And at the 15th one we want the full value of 255. The formula for the greens is exactly analogous. When j is zero, green is zero. When j is 15, we have 15 times 255 over 15 or 255. And for values in between, the green grows as j does. Again, the point of this example was that you need two nested loops to control the rows and the columns.
Let's get some practice with nested loops. This digital clock can display any time of day. It's an American clock, so the hours go from 1 to 12 and minutes go from 00 to 59. We can use a nested loop to print this table of all of the possible times the digital clock can display. We'll need two loops. One will count up the hours, and one will count up the minutes. To make sure that minutes are printed with a zero before them so they fill up two digits, you can use the format string %02d. And if you're not sure where to start while writing your code, think about what code you would write just to print the first line. And how that differs from the code that you would write to print the second line.
Let's try the loop for just the first line first. It seems like the minute isn't changing, only the hour is changing, so I'll declare a loop variable called hour that's going to go through all of the hours. It will start at one, and will go as long as hour is less than or equal to twelve, and we won't skip any. Inside, I want to print the hour, followed by a colon, followed by the minute. And I don't want to print it on a line because all of the hours should go on the same line. Hopefully, you notice I'm using a descriptive name for the counter. And that's because in a moment we're going to have multiple loops and it'll be confusing to see which loop counter is which. Lets try this and see how far we got -if I fix the compiler errors. Okay, this is a good start. We have the first line. Now, the loop for the second line would look like this. The only difference is this number, the minute. So, I'm going to add a loop that counts up the minutes. The minutes will go from 0 to 59. And for each minute, I'll print out all of the possible hours with the current minute. Which I'll pass into print def so I can get rid of my comparison code down here. And let's see how that worked. Well, it's kind of hard to tell, because it's all on one line. What I really want is to have a line break after I finish the first line. So after I've printed out all of the hours, I want to print an empty line to advance down to the next line. And now my table prints out, but there are a couple of issues here. It only goes up to 58 and I said it should go up to 59. And it looks like some is missing up at the top. The stuff that was missing at the bottom Is because of my loop condition. I should have gone to less than or equals and the parts that I'm missing at the top are. Probably because bluej has limited the amount of output that it remembers in the terminal. I'm going to try to going to options and unlimited buffering and then try this again. Now it actually saves all of the data that I wanting to see. Alright, I think we're already for another practice question.
Here is a slightly different nested loops question. How would you write nested loops that print out this pattern of square brackets, without the slashes at the front? So you would print one pair of square brackets on the first row, two on the second row, three on the third row and so on. There are a lot of ways to do this that work, it's a little different from the last problem. In the clock problem the number of columns was the same for each row, but here it varies. When you fill in the code for this, make sure to use this variable for the number of rows. This way we'll be able to change it, and this will work for any number of rows.
I find that it helps me to break these down. So I'm going to start with just imaging printing the first row. This for loop might seem a little bit silly, but once I write it, I can compare it to the one for the second row. For each column, I want to print out a pair of square brackets, but in this case, the columns start at the first column and only go up to the first column. So that'll look like this. The for loop for the second row will look pretty similar except it'll print two pairs of brackets. One for column 1, one for column 2. So this is interesting, instead of something changing inside the loop, it's the actual loop condition itself that's changing form row to row. The threshold is actually the row itself, now these are exactly the same. So I could set row to 1 and do this and then increment row to 2 and do the exact same thing. Which means that I can put this code into a loop. So I'll indent it, remove the extra repetitions and now instead of just setting row to 1, I'll actually loop over all of the rows starting at 1. And going while row is less than or equal to the total number of rows. Let's see how much progress we've made after fixing typos. Well, it's certainly printing some brackets. Let's put some line breaks between the rows. Much better. This was kind of neat. In this problem, we used the outer loop variable to control the number of iterations in the inner loop. The loop condition of the inner loop depends on the loop counter variable for the outer loop.
Another situation in which nested loops often arise is image editing. In the past we've traversed the pixels of an image in a single loop going a row at a time. But there are many situations when it makes more sense to make separate loops for the rows and columns. For example suppose that we want to have this effect where we hide all of the pixels that are outside the circle. In this case, you will want to know what the x and y position of the pixels are. Given values for x and y, you can get a pixel by calling a method called getColorAt and similarly there's a setColorAt that lets you set the color at an x and y location. To get this effect then, we will want to loop through all of the possible x's and all of the possible y's with coordinates x, y and see how close it is to the center point. If this distance is less than the radius of the circle, then we want to leave the pixel untouched. But for a point out here, where the distance is greater than the radius, we want to color this point black by calling setColorAt. Here's the outline of the program. Two nested loops for all values of x and y. Compute the distance. The problem gives you the general formula for computing that and then go ahead and do the coloring.
Let's have a look at how to do this. In this case here we were asked to fill in the ranges for x and y. Let's look at the picture, x moves this way starting at going up to the height minus 1. That gives this loop for x and this loop for y. We were given a formula for the distance, and we can use that down here. Here it is, simply using center x, center y and x, y for the two points. Now if the distance is larger than the radius, then we should color the pixel black and we're done.
A common use for loops is in programs that simulate some activity, such as customers in a supermarket, cars on a road, particles in a physical system. And in such simulations you want to have a degree of randomness, just to model how the real world works. For example, some random time might elapse. Until the next customer arrives, or the customer might buy a random number of articles. So let me show you how to generate random numbers in Java. You construct an object of the Random class. And then you can ask it to give you another integer. You have to give it an upper limit, and you get an integer between zero and n minus one. You can also generate a random floating point number, and then you always get a number that's at least zero but less than one. Let me show you the random number generator in blue jay. Lets make an object by going to the tools menu, use library class, and here in the class field we type in java.util .random and hit the Enter key. Select this constructor and here we have a random number generator. Let's ask it for next double, and we get .05 and change and that looks pretty random. Next time when we ask it we get a different random number, .19 and so on. For integers we'll pick this method over here. We asked for an upper bound and now get a number between zero and course every once in awhile I might get the same number twice. It's random. Now I'll let you play with this. We want to simulate a die and that we get random numbers between one and six. Or actually we might as well model a die with an arbitrary number of sides, and your job is to implement the cast method so that every time that. It's called, it returns a different number between one and the number of sides of the die. There's just a catch. You're going to have to call the next int method, which naturally gives you numbers between zero and n minus one. You want numbers from one to n and I'll leave it to you to figure out how to change one to the other. Has the construction parameter for debugging it's handy to give the random number generator what's called a seed. And as long as you seed a generator with a particular value, then it gives always the same stream of seemingly random numbers. Here we're using a fixed seed, so that you and I get the same answers. And practical applications, people do all sorts of things for a seeding, such as using the time of day. Or when true randomness is desired, they actually have a white noise generator attached to their computer and sample it, so go ahead and implement the cast method.
Here is the solution. All you have to do is call nextInt to get a number between zero and sides minus 1. And then you add 1 to it. And you get a nu, number between one and sides. Let's try it out. We'll make a die with six sides. Call cast, get a three, four, and here's a six.
Let me show you an interesting application of random numbers. You know what pi is. It's the area of a circle with radius one, and of course, there are formulas for computing pi to any desired precision, but that's no fun. We'll compute pi by shooting darts. Specifically, we'll throw random darts into the square. Most of the time we'll hit the circle, sometimes we'll miss, and the ratio of the hits over the tries is going to be approximately the same as the area of the circle, over the area of the square. The square has side lengths 2, so that's 4, and the area of the circle is of course the desired pi. So how do we do this in Java? We generate a random x between minus 1 and 1. A random y also between minus 1 and 1. Compute the distance from the origin and if that distance was at most 1, then we had a hit. If that distance was larger than 1 then we didn't have a hit. So go ahead and complete the program that does this. The biggest challenge is to get the x and y to be random values between minus 1 and 1. Because nextDouble gives you values that are between 0 and 1. So you will need to be creative to adjust this range to the one that you want.
Here is the outline of the dart throwing program. We need to complete the part here where X and Y should be random numbers between minus one and one. So we can't just call generator next double, cause that gets a value between zero and and one, and we want a value from minus one to one. So here's the trick, take the value from zero to one, multiply by two. That gives you a value from zero to two, and then you subtract one, which shifts it to the left by one. Here is the final expression. And of course the very same expression will work for y. Now we need to find whether the point lies in the unit circle. He can compute the distance just like you've done in the telescope problem, but it's actually a little easy in this case. I can take the square of the distance, and check whether that's less equal one, because the square root of one is just one. Don't worry about it if you're just used to formula. It's not a math class, so any correct formula will do. Let's run the program. And once the number of tries, we'll try a million and we get a pretty respectable estimate for pi. And like a said there are better ways of computing pi, but this method of using random numbers for measuring objects is really powerful when the object is irregular. When it's easy to find out whether a point lies in it, but when it's hard to compute the area with traditional means.
Let's use the random class to write a program that uses a loop to simulate tossing a 6 sided die 100 times. The die can come up 1, 2, 3, 4, 5, or 6. Write your program so that it prints the number of sixes that come up. You'll need to import java.util.Random to use a random generator. So then I can help you check your work. We're going to do a bit of a magic trick. Random generators can be seeded. What that means is you give them a number to start rolling from, and then they grow a pattern based on that seed. So the sequence they generate will look random, but if you create another random generator like this, they'll both generate the exact same sequence. So all you need to know about that for now, is to declare your random generator this way. Because the grading code will be expecting a sequence of numbers generated with this seed. If you're not sure what I'm talking about, and you're curious, stick around for a moment and I'll demo what this does. But feel free to skip to the quiz. My trusty code pad. I'll create and seed a random generator, and now I'll do the exact same thing, and make another one. Now if I ask the first generator for an int, I'll get 2. And if I ask the other generator for exactly the same thing, I will also get 2. I can do this again, and they still both agree. So while I'm getting random numbers out if this, The numbers are predictable, so we know what to expect and can help you debug your code.
I've already got my generator set up, and I imported java.util.Random. I know exactly how many times I want to generate a random number, so I'll use a for loop. I want it to generate 100 numbers, and I want to generate numbers between one and six. If I use it this way, it'll give me numbers 0, 1, 2, 3, 4, and 5. So I have to add one. Now I have a die roll, but I don't just want to roll the die 100 times, I want to count how many sixes there are. So if the value is six, I should increment the number of sixes, which means that I had to initialize it, and at the time I had seen zero so far. But I better not do that inside the loop because then I would reset it every time. Now after the loop completes, I'll print out the number of sixes. If you run this you should get
If you take a class in probability theory you will likely hear the story of the Chevalier de Mere. He played two dice games. In game one, he would throw a die four time and bet on at least one six. In game two, he would throw two dice, 24 times and bet on at least one pair of sixes. He thought that the odds of winning both games were 2 3rds. But from experience, he knew or at least his wallet knew that he was wrong. Pascal would later come along to figure out the actual odds and invent probability theory. But we don't care about that right now. We just want to help the poor Chevalier with the drudgery of throwing two dice 24 times. Let's complete this program that simulates game one and game two. These methods should return true if the Chevalier would win. Now there's one subtlety to this problem. In this case, the moment you see a six, you already know that he's won, but our generator is hooked up and doing a particular sequence. So that we can help you check your answer. Make sure to simulate rolling the die four times, even if you already know that he won.
I know exactly how many times I want to roll the die. In this case, it's four. Now I get the die value from my generator, which was already defined up here. The same way we did for the last question, and I remember to add a 1 because this will generate numbers between 0 and 5. Now if the die value is 6, then I know that he won. Initially, I was assuming that I hadn't seen any yet. So he hadn't won yet. So he won would start out as false. And it's a Boolean. Now I return whether he won or not. If any of the die values were ever 6, then he won would be set to true. Otherwise, it'll just be left as false, the way it started. Now for game 2. Game 2 has more stuff going on. I still know exactly how many times I want to run, but this time I have to generate two die values. Now, I only say that he won, if both are 6. So if first roll is 6, and second roll is 6. Then 'heWon' is true and I started out assuming that he haven't won and at the end I've returned the value of 'heWon'. Now, if I won the demo, I should get something like this. It's hard to tell from this output what the exact probability of winning would be. But if you're curious, you could run it a whole lot more times. And use the algorithms that you've already worked with to calculate the total number of wins and compare it between game 1 and game 2. Good work on this problem.
Here's another way of estimating Pi. Take a one inch needle, and repeatedly drop it on a sheet of ruled paper whose lines are two inches apart. Call it a hit if the needle is hitting one of the lines. If you do this a whole bunch of times, Pi will be approximately equal to the number of drops over the number of hits. If you're curious about the details, you can check out a Wikipedia page on this. Clearly, dropping a needle on the floor, and measuring whether it touches a line thousands of times is not a job that anybody wants to do. Thankfully, we can make our computer simulate the experiment for us. We randomly generate a yLow, or the lowest point of the needle, which should be somewhere between zero and two. Then we calculate an angle a between zero and a hit. Now do this many thousands of times and you'll have an estimate for pi. So again you want to randomly generate y low, randomly generate and angle a And then calculate y high. Which will be y low plus the sine of the angle. And be careful, because math.sine takes radiance, not degrees. Math.toradians can help you with the conversion. You can always review the math facts sheet if you don't quite remember how this works. Here's the start of the program to do this. And when you put in the number of tries, I recommend using a large number. At least 30,000. When you write this code, make sure to use the variable tries to control how many times you drop the needle.
First I want to generate a yLow. .nextDouble will give me something between 0 and 1, but I want it to be between 0 and 2, and I definitely want a double. Because yLow could be anywhere between 0 and 2. Then I'll generate another double between 0 and 180, and I'll use both of those to calculate yHigh, which is yLow plus the sin of A in radiance. Now if yHigh is greater than or equal to times, so I'll highlight it all, hit tab to indent, and then wrap it in a for loop. And I'll need to actually declare hits. Which starts off at 0. All right. Let's try this. I'm going to try it 30,000 times. 3.13. I guess it's okay. It's not great. Let's try it again. 300,000. It's a little closer to 3.14. How about long. 3.141. We're getting much closer now, but it took 30 million flips. And Pi is actually 3.1415 something or another. So we've only got three digits correct. I think anyone who tried to do this without a computer would be flipping needles for their entire life. This method was simply not viable without a computer.
It's a fact of life that no matter how hard we try our programs have bugs. In the olden days, computers had real bugs. For example, the moth that you see here got trapped in a relay of the very first computer in the United States causing it to malfunction. But of course, nowadays, the bugs are usually the fault of the programmer by not having thought through everything. And that's normal. You can find simple bugs by putting print statements into your code, printing out everything in sight. Seeing what goes wrong, and then removing the print statements again, but that's no fun. There's a nifty software tool called a debugger, that you can use to control your program. Stop it and look around, and then find out what it actually does as opposed to what you think it's been doing. Using a debugger is pretty simple, there are three key concepts that you need to master. You can set breakpoints in your program, and then that the debugger will stop when the program reaches a breakpoint. You can then step through your program one instruction at a time. And you can even step inside method calls. And finally, you can inspect the contents of variables. So if you have a variable, you can ask the debugger what's inside at this point. So, these are the three key concepts that you want to keep in mind. Let me show you how to do these in BlueJ. This example program, tries to count the syllables in a word. Let me show you. enter a few words. And as you can see, the program doesn't seem to be doing so well. Hello has two syllables, not one. Yellow also has two. Oh, peach has one. Before debugging this program, let's have a quick look at algorithm is used to count the syllables. Here is our algorithm. We look for groups of vowels. There's one here, another group of length one, and another one, and we count the groups. This word has three syllables. However, here we'll count this group, but we won't count an "e" at the end, because nice only has one syllable. That second rule trips us up with some very short words such as the. We're not counting the E would give us zero syllables and we'll say if that happens we'll change the count to one. To summarize, we count vowel groups but not a final e and if that count gives us zero, we change it one. Now on, just seeing the debugger in action. Open the syllable counter program in BlueJ, and I really suggest that you do that with me. Click in this column here next to the first line of the main method and you'll see a tiny stop sign, that's a break point. Now, run the program in the usual way, and now the debugger wakes up. We've hit this break point and what you see over here is the console that controls the debugger. These buttons here, let you step through the program. Let's try it out. We'll click on step and you can see that we're now in the next line. We click on step again, oh, now it has produced the output here. And we're back where the little arrow is. Now when we step nothing seems to be happening, the arrow is gone. That's because right now, we're reading input. Let's look at the terminal window and here it is waiting for the input. I'll just apply it. Now I hit Enter, and you can see the debugger window again. And back here our program has advanced by one step. Let's bring this to the front. And we're past here. Now to make sure that you are trying this at home, let me ask you to do exactly what I did. Set the break point here, enter hello yellow peach, keep signal stepping. And I'd like you to tell me, how many times are you hitting this line, the call to the constructor, before you get out of the loop.
He reached this line three times. Let me show you. Here is the first time after having entered the input. Let's keep stepping. Here is the second time. Here is the third time. And now, watch carefully what happens, we're done with the loop. So we've been here three times.
You've just seen how to step over each of these lines. But it didn't really show us where the program misbehaved. The hard part goes on in this countSyllables method. So we want to get inside it. For that we need to use the second step button, the step into button. Let me restart the program and do just that. Here we are again at the first line of the program, we enter the input again, we step over the constructor, and now, going to hit the step into button, and that opens up the Word class. Then we have to look around a bit to find the arrow. We're right now, here. Now we can keep stepping, and that gets us through the lines of countSyllables. So the point is, you use step to stay within the same method, and step into to go inside a method. Now all that stepping can get tedious and sometimes it's better to set more break points and run the program at full speed until one of them gets hit. Let's practice that. You see this line where it says count plus plus? Set a break point by clicking here and now go ahead and restart the program. You'll hit the first break point in main. When you hit it, click the Continue button. So that your program continues at full speed. Supply the same input, and then your program will wake up at this break point. Hit Continue again and keep doing that, and tell me how many times you get the this break point.
You should get that three times. Let's try it out. Run the program, here's the initial break point, click on continue, supply the input and here we get to the break point for the first time. Click continue for the second time, for the third time and now our program has terminated.
You've seen how to use breakpoints in single stepping, to run your program in slow motion. And of course what you really want to be able to do is look at variables. I'm going to rerun the program until I hit this breakpoint, here I am, and over here in this window, you can see the settings of the local variables. You can see that count is 0, end is 3, and this Boolean inside vowel group is false. Up here you can see the settings of the instance variables, of the class whose method we're currently in. Go ahead at home and do exactly what I just did. Run the program until this breakpoint. Then click continue one more time until this breakpoint is reached for the second time, and tell me what is the value of end when you reach it.
At the second stop, end is four. Let me show you. I click on continue, I reach the break point again, and over here, I can see that end has the value four. That's how you inspect variables in the BlueJ debugger. Now, we still haven't found the bug in our program, but Sarah will do that with you in the following segments.
In the debugging demo, Ki, noted that the word hello, got truncated to hell. The place where text is set is in the constructor, so something must be wrong in this constructor. Let me set a break point to check it out. I just clicked next to the line I want to stop on. Now, if I run the syllable counter, give it hello yellow peach and hit Enter. It pops up my debugger, and my code shows the line that the debugger is stopped on. So far, not a whole lot has happened. We've just converted S to lower case and put it in text. I want to skip these loops and get to the juicy part and figure out what they computed. So, I'll just single step until I get to the last line. Try that out. What will the local variables, i and j be equal to if you step all the way to this last line? You'll really need to do this in BlueJ because the Udacity IDE doesn't have a debugger. So, if you run syllable counter and enter hello yellow peach and then step to the last line of the constructor, what is the value of I? How about j?
If I pick up where I left off and I step and get to the last line, I can see all of my local variables right here. The string s contains hello, i is 0 and j is 4.
I and j both sort of look right to me. If i is meant to be the first letter that we include, that would be h, that would be good, and if j was the last letter that we were supposed to include, then that would be 4 which would be o. Let's try stepping through the last line. When you step past that last line, what is the value of text?
If I pick up where I left off and hit step, I can see now, that my instance variable text, has been set to health.
So what was the error? Can you fix the constructor so that it initializes the text instance variable without losing letters?
The problem is that the substring method includes i, and excludes j. So when we have j as 4, substring stops copying over the letters, at 3. I can fix this by adding 1. This is a pretty typical error.
So now I'm going to stop this program since I think I've fixed it, by clicking Terminate. If I put in hello yellow peach again, which test cases will pass now? Will hello pass? How about yellow? And peach?
The only one that passes is peach. If I pick up where I left off, and click enter, I get that the syllables in all of these words is 1. But peach is the only one for which that's actually correct. Hello and yellow should both have two syllables.
The program must have more than one bug. Let's find out what's wrong with the syllable calculation. Before we go too far, let's make sure that text is actually hello. I'll place the break point in the beginning of syllable calculation. I can't place it right here because the declaration of the method isn't really a line that you can run. Now, I'll run the program. It still thinks it's running, so I'll just hit terminate and now I'll run it. Or keep trying that same sentence, and it stopped and it looks like the text is right this time, it's hello. That's good. So, I'll keep stepping. Probably the most interesting part is when count gets incremented. So, let's try stepping it till that happens. Stepping, stepping, keep going, all right, we're getting closer, we're now inside this four, inside the if. We'll step to the next one. Alright, and now I'm at count. What is the value of letter right now? If you step until count is incremented, right here, what is the value of letter?
Letter should be e. I can see that, by opening this up a little bit more, and then string letter equals e appears under my local variables.
Okay, that makes sense. When we see a vowel, that means it's the start of a new vowel group, which would mark that we have another syllable. Any time we see a vowel that doesn't have another vowel before it, we want to increment count. Now I want you to keep stepping until letter is o, and deposit this line where we set letter to text.substring. If you keep stepping after that line, what happens? Is the first if statement entered? If so, is the second if statement entered, is count incremented, is inside vowel group set to true?
Well let's see. I'm stepping, and now I've got letter is equal to o. If I continue stepping, I go inside the first if statement. So I'll check that one off. Now if I step again, I didn't go into the second if statement. So the count isn't incremented. And insideVowelGroup isn't set to true.
That was bad. It shouldn't have skipped from here back up to the next letter. The o in Hello is the beginning of a new vowel group, but the program didn't count it that way. It looks like it didn't run these lines, because inside vowel group is already true. But that's a mistake. Inside vowel groups should be false, because the letter before o was l, which is a consonant. How would you fix that? I'll make a few suggestions and you can pick the one you think will work and implement it. Should we remove the condition, if not inside vowel group, so that we always increment counts for vowels. We could change the condition of the inner if block from not inside vowel group to count equals equals 1. We could set inside vowel group to false right after count plus plus or set inside vowel group to false when letter is not a vowel.
The answer is, we want to set inside vowel group to false when letter is not a vowel. If we always increment count for vowels, then words like peach would be counted as having two syllables. These two vowels, e and a, are in the same vowel group. So even though there are two, we only want one syllable. If we change the condition of the inner if block from inside vowel group to count equals equals one, Will only increment count when there's already one syllable counted. So we'll never increment count. If we set inside vowel group to false right after count++, we'll never be inside of a vowel group. Every vowel would be counted as a syllable, so this would be a lot like the first option. It wouldn't work well for words that have two vowels together. The last answer is the correct one. When we see a non-vowel, we want to change insideVowelGroup to false. For example, in hello, we see the e and enter a vowel group, but then we see an l, so that ends the vowel group.
So now go ahead and fix this code. Once you fix the code, which test cases pass. Does hello pass, how about yellow and peach.
Hello, yellow, and peach all pass now. If I go to the code, and when the letter is not a vowel, I change insideVowelGroup to false. Now if I make sure my program is not still running, and then compile, and run it again, it seems like it's counting all of the syllables correctly for these words.
So is this program free of bugs? It would be nice if a debugger just took all of the bugs out but it can't tell us. As Edgar Dijkstra said, testing can only show the presence of bugs, not their absence. I bet you can think of a test case that demonstrates another bug in this code. If so, post it on the forum.