Why is my loop skipping this conditional? - java

Time for my daily newbie Java question :-D
I must not be understanding conditionals in a while loop correctly.
I have this:
while (true){
if (){
...
} else {
...
}
if (){
...
} else {
...
}
if (SENTINEL){
break;
}
}
The first if/else statement is working, and the sentinel is working, but the second if statement gets skipped. If I flip the first and second if statement, then the first if statement still always gets executed and skips the second. What am I missing?
Can I have two if/else statements in one block like this?
I'll include the whole code, though it's pretty ugly, and I'm sure I'll get lots of people telling me better ways of doing this. I don't mind learning better ways, but for now, I just want an answer to this looping question. thanks!
public class FindRange extends ConsoleProgram {
private static final int SENTINEL = 0;
int value = 0;
int highNumber = 0;
int latestValue = 0;
int lowNumber = 0;
public void run() {
addNumbers();
}
private void addNumbers(){
value = readInt("Enter number:");
while(true){
if (value == SENTINEL){
break;
}
latestValue = readInt("Enter number:");
getHighNumber();
getLowNumber();
if (latestValue == SENTINEL){
break;
}
}
println("High Number is "+highNumber+".");
println("Low Number is "+lowNumber+".");
}
private void getHighNumber(){
if (latestValue >= value){
highNumber = latestValue;
}else {
highNumber = value;
}
}
private void getLowNumber(){
if (latestValue <= value){
lowNumber = latestValue;
}else {
lowNumber = value;
}
}
}

Are you trying to find the minimum and maximum of a series of numbers? If so, you should definitely use Math.min() and Math.max(). It's much clearer that way and you can do away with the if statements. It's also simple enough to do it in the loop with local variables instead of fields.
The common idiom is something like this:
minValue = Math.min(minValue, candidateValue);
maxValue = Math.max(maxValue, candidatevalue);

It's possible that the behavior you're seeing comes from the fact that you are always comparing the latest value to the initial value. The initial value will never change-- so if you put in the following input:
20, 60, 50
the high value that gets reported would be 50. That's because 50 is the most recent value to be greater than 20. I think you probably mean to compare the latest value to the high value, no?

You can definitely have 2 if/else blocks within the loop; however if your sentinel gets hit the loop will exit.
Posting the entire block would help.
What will happen (after reading the posted code) is when any new value you enter within the loop is greater than the original value, lowNumber is set back to the original. So for example if your input is:
7 6 5 8
Your corresponding low number values will be:
7 6 5 7
Which is incorrect. What you could do is toast the "value" variable altogether, set your low and high to the original value, then compare latest with low and high in the get* methods.

Shouldn't you be setting value = latestValue at the bottom of your while loop?
Value never gets updated after the initial read... maybe something like this:
public class FindRange extends ConsoleProgram {
private static final int SENTINEL = 0;
public void run() {
addNumbers();
}
private void addNumbers() {
int value = 0;
// Set this to highest possible value
int highNumber = Integer.MIN_VALUE;
// Set this to lowest possible value
int lowNumber = Integer.MAX_VALUE;
while (true) {
value = readInt("Enter number:");
if (value == SENTINEL)
break;
lowNumber = Math.min(lowNumber, value);
highNumber = Math.max(highNumber, value);
}
println("High Number is " + Integer.toString(highNumber) + ".");
println("Low Number is " + Integer.toString(lowNumber) + ".");
}
}

Related

Chat Bot, last part

Just one last part remaining in the ChatBot. I need to figure out a way to modify the chatbot class so
that it occasionally (say, 30% of the time) returns a randomly-­‐generated standard reply to user input one of at least five possible replies, like “LOL”, “OMG”, “You don’t say”, “Really?”, or “I see”.
Edit: Applied recommended changes:
import java.util.Random;
import java.util.Scanner;
public class ChatBot
{
private int responseCount = 0;
public String getResponse(String value)
{
String X = longestWord(value);
this.responseCount++;
if (responseCount == 10)
{
return "Sorry, but our time is up. I can't talk with you any longer.";
}
if (value.contains("you"))
{
return "I'm not important. Let's talk about you instead.";
}
else if (X.length() <= 3)
{
return "Maybe we should move on. Is there anything else you would like to talk about?";
}
else if (X.length() == 4)
{
return "Tell me more about " + X;
}
else if (X.length() == 5)
{
return "Why do you think " + X + " is important?";
}
else if (X.length() <=9)
{
return "Now we are getting somewhere. How does " + X + " affect you the most?";
}
return getRandomResponse();
}
public String longestWord(String value){
Scanner input = new Scanner (value);
String longest = new String();
longest = "";
while (input.hasNext())
{
String temp = input.next();
if(temp.length() > longest.length())
{
longest = temp;
}
}
return longest;
}
private String getRandomResponse()
{
String [] responses = {"OMG", "LOL", "You don't say", "Really?", "I See"};
return responses [(int)(Math.random() * responses.length)];
}
}
The problem is, it keeps returning the same response, instead of one of the five responses given. Any help would me much appreciated, thank you!
Edit:It's now giving only the random responses, and overriding every other response in the getResponse() method.
Given your logic, your getRandomResponse method should always return "OMG". This is because on the first run of the loop in that method, counter = 1. Thus the first if statement will run and will return "OMG" exitting the method. A nicer equivalent might putting all teh responses into an array and returning a random value from it, rather than doing somehting strange with iteration:
String[] responses = {"OMG", "LOL", "You don't say", "Really?", "I See"};
return responses[(int)(Math.random() * responses.length)];
In getRandomResponse, you make a random number generator using Random(), but you never use it. Then in your for loop, you execute your decision-making tree but use a variable counter that always begins at 0. Then on the first time through your loop, the first if statement will execute because 0 < 5, so "OMG" is returned.
Edit: I just noticed something else that is not going to work in your code:
Random randomNumber = new Random();
for (int counter =0; counter<10; counter++)
{
counter = randomNumber.nextInt();
You're trying to use counter to do two different things: you are trying to run this loop 10 times, but you're also using it to store random values.

For loop error, how do i fix this?

for(int x = 0;x<14;x++){
day[x]= theSheet.changeLetters(day[x]);
}
public String changeLetters(String entering){
if(entering.equalsIgnoreCase("a")){
entering = "10";
} else {
if(entering.equalsIgnoreCase("b")){
entering = "11";
} else {
if(entering.equalsIgnoreCase("c")){
entering = "12";
} else {
if(entering.equalsIgnoreCase("d")){
entering = "13";
} else {
if(entering.equalsIgnoreCase("e")){
entering = "14";
} else {
if(entering.equalsIgnoreCase("f")){
entering = "15";
} else {
if(entering.equalsIgnoreCase("g")){
entering = "16";
} else {
if(entering.equalsIgnoreCase("h")){
entering = "17";
}
}
}
}
}
}
}
}
return entering;
}
Says the error is here if(entering.equalsIgnoreCase("a")) and in the for loop I am using to run the method. I'm trying to change the letter put into the string into a number.
Can anyone explain to me where the error might be? I'm having trouble spotting the issue. it lets me enter the letters fine but it has an exception once it gets to this for loop and runs this method.
why don't you use
if (condition) {
// ...
} else if (condition2) {
// ...
} else if (condition3) {
// ...
}
// and so on
to make your code more readable. Your nested conditions are a mess. If you fix them, you might as well fix the error (if it's in the part of code you showed us).
Also add
System.out.println("Entering = '" + entering "'");
at the beginnig of your method to see if really receives what you are expecting it to receive.
Ok according to
Yes the code is being initialized in a for loop before that one using
for(int x =1;x<8;x++){ day[x-1] = JOptionPane.showInputDialog("Enter
hour pairs for day "+x +".\n Enter the first digit: "); day[x] =
JOptionPane.showInputDialog("Enter the second digit: "); } the letters
being put in the array are then changed into numbers using the for
loop posted earlier.
You have a logic error. You are overwriting previous entries and not filling the array up to 14 items. So items after 8 are left null, thus the NullPointerException.
Try this:
String[] day = new String[14];
for( int i = 0; i < 14; i+=2 ) {
day[i] = JOptionPane.showInputDialog("Enter hour pairs for day "+(i/2+1) +".\n Enter the first digit: ");
day[i+1] = JOptionPane.showInputDialog("Enter the second digit: ");
}
As a bonus, you can simplify the if-else code with:
public String changeLetters( String entering ) {
return String.valueOf(entering.toUpperCase().charAt(0) - 55);
}
As #jlordo already stated avoid nested if-statements with such a depth.
As an alternative you might use a switch statement in combination with an enum like this - although the following approach consists of more code it is more general and better suitable to be extended (e.g. using all letter of the alphabet and beyond) and it would make the code much more readable and more appropriate with respect to what you want to express.
Letter letter;
if (letter.equals("a")) letter = Letter.a;
if (letter.equals("A")) letter = Letter.A;
// and so on ...
switch (letter) {
case a : {
// do some code here ...
break;
}
case A : {
// do some code here ...
break;
}
// and so on ...
}
public enum Letter {
a (1),
A (2),
b (3),
B (4),
c (5),
C (6);
// and so on
private final int index;
Letter(int i) {
index = i;
}
public int getIndex () {
return index;
}
}
Note that if you're using Java 7 you can use the switch statement even without the enum since it accepts strings as well like this:
switch (entering) {
case "a" : {
// ...
}
// ...
}

Art & Science of Java Chapter 4, Exercise 8

I'm trying to write a program that does a countdown to liftoff, but using a while loop, instead of an for loop.
So far, all I succeed in doing is creating an infinite loop, even though I'm using the same basic principles as the for loop code.
import acm.program.*;
public class CountDownWhile extends ConsoleProgram {
public void run() {
int t = START;
while (t >= 0); {
println(t);
t = t--;
}
println("Liftoff!");
}
private static final int START = 10;
}
There are two error in your code. And that's the reason you're getting an infinite loop
1.
while (t >= 0);
You shouldn't add a semi-colon after this line, because it is actually means a while loop with nothing in it.
2.
t = t--;
you can check this question to learn more about this syntax : Is there a difference between x++ and ++x in java?
In short, the value of t-- are still 10, so t = t-- dose not change the value of t.
The loop should look like this:
while (t >= 0) {
println(t);
t--;
}
println("Liftoff!");
The first problem is the ; after the while loop. Try removing the ;...
public void run() {
int t = START;
while (t >= 0); { /// <------ Problem 1. Correct: while(t>=0)
println(t);
t = t--; /// <------ Problem 2. Correct: t--;
}
println("Liftoff!");
}
and the second problem is
t=t--;
as the value of t remains unchanged.

Project Euler prob. 3 IndexOutOfBoundsException

I'm trying to use a Sieve of Eratosthenes method for finding the largest prime factor of a large number (problem 3 in Project Euler).
My syntax seems to be correct, and i am using Long (not int), but I'm getting the following error message:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at problem3.ProblemThree.Factor(ProblemThree.java:49)
at problem3.ProblemThree.Recursion(ProblemThree.java:37)
at problem3.ProblemThree.main(ProblemThree.java:83)
I don't know why this is happening. Could somebody please tell me what I'm doing wrong here?
package problem3;
import java.util.List;
import java.util.ArrayList;
public class ProblemThree
{
//initializing variables and lists
long factorNo;
long nowTesting;
int i;
List<Long> allPrimeList = new ArrayList<Long>();
List<Long> ourPrimes = new ArrayList<Long>();
ProblemThree(long x) //constructor; the input "x" is the number whose highest prime factor is being sought
{
factorNo = x;
}
void initialize() //use the workaround initialization (add 2 to the allPrimesList, set nowTesting to 3).
//If the factorNo is even, add 2 to the primes list
//TODO: need more elegant solution
{
allPrimeList.add((long) 2);
nowTesting=3;
if(factorNo % 2 == 0) ourPrimes.add((long) 2);
i = 0;
}
void recursion() //keep factoring the next nowTesting until the next nowTesting is greater than half of the factorNo
{
while (nowTesting <= (factorNo/2))
{
nowTesting = factor(nowTesting);
}
System.out.println(ourPrimes);
}
long factor(long t) //The factorization algorithm. Lists all the factors of long t
{
nowTesting = t;
// Line 49:
if ((nowTesting % allPrimeList.get(i)) == 0)
{
i = 0;
return (nowTesting + 2);
}
else
if(i <= allPrimeList.size()) //if we have not yet reached the end of ourPrimeList
{
i++;
return nowTesting;
}
else //if the end of ourPrimeList has been reached without a single modulus==0, this number is a prime
{
allPrimeList.add(nowTesting);
if(factorNo%nowTesting==0) //if the nowTesting is a prime factor of factorNo, it will be perfectly divisible
{
ourPrimes.add(nowTesting);
}
i=0;
return (nowTesting+2);
}
}
public static void main (String[] args)
{
ProblemThree pt = new ProblemThree(600851475143L);
pt.initialize();
pt.recursion();
}
}
thank you everyone for patiently wading through my code, I realize that it must have been excruciatingly painful :)
I have just solved the problem. My previous approach seems very complicated in retrospect. This is the final solution I used, quite a bit more elegant, although it still has room for improvement:
//second attempt from the ground up!
package problem3;
public class BiggestPrime
{
long lInput;
long factorTest;
long currentHeight;
boolean divided;
public BiggestPrime(long n)
{
factorTest = 2;
currentHeight = n;
System.out.println("The prime factors of " + n + " are:");
while (factorTest<currentHeight)
{
if (divided == true) {factorTest = 2; divided = false;}
if (factorTest > currentHeight) {System.out.println("factorTest is greater than currentHeight; breaking"); break;}
if (currentHeight%factorTest==0)
{
System.out.println(factorTest);
currentHeight /= factorTest;
divided = true;
}
else { factorTest = factorTest + 1L; divided = false;}
}
if (factorTest == currentHeight)
{
System.out.println(factorTest);
}
System.out.println("The end");
}
public static void main (String[] args)
{
BiggestPrime bp = new BiggestPrime(600851475143L);
}
}
An interesting approach. Of course, nobody should solve your Euler challenges. But did you know that the second time, you enter 'factor' nowTesting is 3?
// The factorization algorithm. Lists all the factors of long t
long factor (final long nowTesting)
{
System.out.println ("entering factor: " + nowTesting);
Minor ideas:
allPrimeList.add ((long) 2);
can be written:
allPrimeList.add (2L);
and you pobably recognized the "final" in front of the 'long' parameter in factor? It helps reasoning about code, if you mark everything which isn't changed final. In practise, the consequence is, that your Javacode is cluttered with 'final' modifiers, but that's how it is. It's a sign of good code - maybe not of good design. Final could have been the default.
At line 49, shouldn't you be checking if nowTesting is divisible by i, not the ith element of allPrimes?

Finding Numbers in a Row?

I am working on an algorithm, and I need to be able to pass in a List and see if there are four numbers in a row at any point in the list.
I have been struggling with an easy way to do this... Here is the basic idea.. I would like the fourNumbersInARow() method to return true:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Numbers {
/**
* #param args
*/
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<Integer>();
for(int i = 0; i<10; i++){
numbers.add((new Random().nextInt()));
}
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
System.out.println(fourNumbersInARow());
}
private static boolean fourNumbersInARow() {
}
}
Use two variables: last_value and row_count. Going through the list one by one, always look whether the current value is exactly one bigger than the last_value; if yes, increase row_count, if no, reset it to 1. In any case, set last_value to the current value and loop. If at any point row_count becomes 4, return true. If you reach the end of the list, return false.
EDIT: changed counter range to start at 1
Here's an implementation in Java.
static boolean fourNumbersInARow(List<Integer> list) {
int last = 0xFACADE; // can be any number
int count = 0; // important!
for (int i : list) {
if (i == last + 1) {
if (++count == 4) return true;
} else {
count = 1;
}
last = i;
}
return false;
}
Unlike others, this resets the count of numbers in a row to 1 when the sequence is broken (because a number on its own is 1 number in a row). This allows for easier treatment of the first iteration where technically there is no previous number.
In pseudocode:
consecutiveCount = 1
lastNumber = firstElementInList(list)
for (number in list.fromSecondElement()):
if (number - lastNumber == 1):
consecutiveCount++
else:
consecutiveCount = 1
if (consecutiveCount == 4):
return true
lastNumber = number
return false
The bottom line is, you'll want to keep track of the last number in that was in the list, and compare it with the current number to see if the difference is 1. In order to remember the last number, a variable such as lastNumber is needed.
Then, in order to keep track of how many consecutive numbers there have been there should be a counter for that as well, which in the example about is the consecutiveCount.
When the condition where four consecutive numbers have occurred, then the method should return true.
This sounds a little like a homework question, so I don't want to write out a complete solution. But in your method just iterate through the list. Take the first number and see if the next number comes after the current, if so then set a variable flag with the start position and the current number, on the next iteration through the loop check to see if that value is before the previous the value etc... Once four in a row are found, break out of the loop and return true. If you encounter a number that is no chronologically correct then set a flag(start location) to null or negative and start the process over from the current location in the list.
Check this Code, this will return true if there a sequence of 4 numbers and else false otherwise
public class FindFourSequence {
public boolean isFourinRow(ArrayList seqList) {
boolean flag = false;
int tempValue = 0;
int tempValue2 = 0;
int tempValue3 = 0;
int tempValue4 = 0;
Iterator iter = seqList.iterator();
while(iter.hasNext()){
String s1 = (String)iter.next();
tempValue=Integer.valueOf(s1).intValue();
if(!(iter.hasNext())){
break;
}
String s2 = (String)iter.next();
tempValue2=Integer.valueOf(s2).intValue();
if(((tempValue2-tempValue)==1) || (tempValue-tempValue2)==1){
if(!(iter.hasNext())){
break;
}
String s3 = (String)iter.next();
tempValue3=Integer.valueOf(s3).intValue();
if((tempValue3-tempValue2)==1 || (tempValue2-tempValue3)==1){
if(!(iter.hasNext())){
break;
}
String s4 = (String)iter.next();
tempValue4=Integer.valueOf(s4).intValue();
if((tempValue3-tempValue4==1) || (tempValue4-tempValue3)==1){
flag = true;
return flag;
}
}
}
}
return flag;
}
public static void main(String[] args) throws Exception {
ArrayList aList = new ArrayList();
boolean flag = false;
FindFourSequence example = new FindFourSequence();
Random random = new Random();
for (int k = 0; k < 25; k++) {
int number = random.nextInt(20);
System.out.println(" the Number is :" + number);
aList.add("" + number);
}
/* aList.add("" + 1);
aList.add("" + 2);
aList.add("" + 3);
aList.add("" + 4);*/
flag = example.isFourinRow(aList);
System.out.println(" the result value is : " + flag);
}
}

Categories