0

I recently started learning java in uni and a task we had to do is to understand recursion and add factorial function to this Polish notation code. I have tried various methods, and this is the latest one:

public class PolishNotation {

    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            System.out.println("Please enter the operators");
            System.out.println("for operators +, -, *, and !");
            System.out.println("Leave spaces between all operators and digits");
            System.out.print("expression: ");
            System.out.println("value = " + evaluateEXP(scanner));
        }
    }

    //input contains the expression user has entered
    public static int evaluateEXP(Scanner scanner) {
        //checks if there is another digit after current one
        //allows expression to be looked at from right to left
        if (scanner.hasNextInt())
            return scanner.nextInt();

        //if there is another digit after current one then
        //operands and operators are established
        char operator = scanner.next().charAt(0);
        int operand1 = evaluateEXP(scanner);
        int operand2 = evaluateEXP(scanner);
        return evaluateOP(operator, operand1, operand2);
    }

    //operator has to be one of +, - , * or ! otherwise error is given
    private static int evaluateOP(char operator, int operand1, int operand2) {
        if (operator == '+')
            return operand1 + operand2;
        if (operator == '-')
            return operand1 - operand2;
        if (operator == '*')
            return operand1 * operand2;
        if (operator == '/')
            return operand1 / operand2;
        if (operator == '!')
            //if ! used then uses factorial method
            return factorial(operand1);
        //RunTimeException allows to return an error string in a int "type" method
        throw new RuntimeException("operator not allowed for this language");
    }

    private static int factorial(int n) {
        return n == 1 ? 1 : factorial(n - 1) * n;
    }

}

There are no errors, but a result does not come out so I am guessing that it's stuck on an infinite loop. The idea of this code is that if I did ! + 3 2 it should do !5 so return 120 and I cannot use while or for loops. The rest of the operands work, it's just the factorial that doesn't.

rene
  • 41,474
  • 78
  • 114
  • 152
jay park
  • 3
  • 4
  • Possible duplicate of [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Max Vollmer Oct 11 '18 at 19:44
  • @MaxVollmer Bro I just started uni, haven't learnt all parts/aspects of debuggers yet. Baby steps, but I'll read into it and try my best to understand it. But I think if I still use a debugger I probably still won't understand my mistake. New to Java. But honestly doubt it's a duplicate, it's not a question on debuggers. – jay park Oct 11 '18 at 19:48
  • That's why I marked this duplicate, so you can learn how to use debuggers. And believe me, using your debugger on this code will make you understand the mistake. – Max Vollmer Oct 11 '18 at 19:50
  • @MaxVollmer turns out the IDE I'm using doesn't even have a debugger – jay park Oct 11 '18 at 19:54
  • And what IDE would that be? – Max Vollmer Oct 11 '18 at 19:54
  • 1
    just realised technically not IDE. I'm using a simple text editor in linux (my uni insists) and saving it as a .java file and then compiling and running it on terminal – jay park Oct 11 '18 at 19:59
  • Ah, well, you might want to look for a proper IDE with a debugger, then. – Max Vollmer Oct 11 '18 at 20:09
  • @jaypark just modify your **evaluateOP** method and factorial definition part to make this code work. – suvojit_007 Oct 11 '18 at 20:09
  • `evaluateOP` waits for 3 inputs. Do you by any chance only enter `!` and one integer? Because if you do, it will wait for a second integer. – Max Vollmer Oct 11 '18 at 20:12
  • @MaxVollmer will try – jay park Oct 11 '18 at 20:13
  • @MaxVollmer even if I add more than one digit it still won't work. I am very new to programming, I still won't understand debugging it if I did it. Maybe my code is so hard there is no solution – jay park Oct 11 '18 at 20:20
  • Your code isn't hard at all. I just pasted your code into my IDE. When I enter `! 5` the application waits for further input, as I assumed. Entering `! 5 5` returns `120` and the application terminates. – Max Vollmer Oct 11 '18 at 20:30
  • @MaxVollmer that's strange, when I entered ! + 3 2 I still had the same error even though 120 should have been returned. This code is so hard, very difficult excercise. – jay park Oct 11 '18 at 20:33
  • That's not strange at all. `! + 3 2` is equivalent with `! 5`, which, as stated above, will make your code wait for another integer. That's the bug. `! + 3 2 5` or `! + 3 2 1` or `! + 3 2 243654` works. – Max Vollmer Oct 11 '18 at 20:35
  • @MaxVollmer that makes a lot of sense! But I don't see why It wants two integers, the factorial method only has one arguement – jay park Oct 11 '18 at 20:37
  • Because you call `evaluateEXP(scanner);` two times. It never gets to the factorial function. It is waiting before that. – Max Vollmer Oct 11 '18 at 20:37
  • (This is btw something a debugger would immediately show you, it would show you that it is waiting in that line.) – Max Vollmer Oct 11 '18 at 20:38
  • @MaxVollmer I will definitely look into and ask my professors about debugging. Seems very useful but for some reason he insists on no IDEs and using text files as we are "beginners". How would I fix this code? Feel like I could learn a lot from you. – jay park Oct 11 '18 at 20:43
  • Just use an IDE with a debugger in parallel to the text editor/terminal stuff if your prof insists. No harm in learning more than the uni demands. – Max Vollmer Oct 11 '18 at 20:44
  • I'm actually at the uni labs right now, not allowed to just download softwares unfortunately (Just tried to install Intellij). Could you explain the solution and exactly how it works if that's not too much of a bother? – jay park Oct 11 '18 at 20:47
  • That's unfortunate. If you have the ability, at least install something at home. Of course I don't know the reason behind your professor's guidelines, but these things usually are why university graduates have a harder time landing a good job in the industry than people with vocational education. – Max Vollmer Oct 11 '18 at 21:13
  • @MaxVollmer some experienced programmers did complain but he insisted that real programmers learn java this way first. I'm will be getting an IDE at home for further independent practice. Thank you! – jay park Oct 11 '18 at 21:21
  • *"real programmers"* oh boy. Guess I am not a real programmer then ¯\\_(ツ)_/¯ Good luck with your studies! – Max Vollmer Oct 11 '18 at 21:22

3 Answers3

1

The problem is that in evaluateEXP your code always expects 2 operands. However ! only takes one operand, so if you enter something like ! 5 it will wait for more input.

The solution is to check if the operator is unary or binary, and only accept one operand if it is unary. Here are a few ways to refactor your code to achieve this:

1) Check the operator in the evaluateEXP method and only get the second operand if it's binary (in your case not !):

//input contains the expression user has entered
public static int evaluateEXP(Scanner scanner) {
    //checks if there is another digit after current one
    //allows expression to be looked at from right to left
    if (scanner.hasNextInt())
        return scanner.nextInt();

    //if there is another digit after current one then
    //operands and operators are established
    char operator = scanner.next().charAt(0);
    int operand1 = evaluateEXP(scanner);
    int operand2 = 0;
    //only take second operand if operator is not unary
    if (operator != '!') {
        operand2 = evaluateEXP(scanner);
    }
    return evaluateOP(operator, operand1, operand2);
}

2) Pass the scanner to evaluateOP and let it take the operands directly:

//input contains the expression user has entered
public static int evaluateEXP(Scanner scanner) {
    //checks if there is another digit after current one
    //allows expression to be looked at from right to left
    if (scanner.hasNextInt())
        return scanner.nextInt();

    //if there is another digit after current one then
    //operands and operators are established
    char operator = scanner.next().charAt(0);
    return evaluateOP(operator, scanner);
}

//operator has to be one of +, - , * or ! otherwise error is given
private static int evaluateOP(char operator, Scanner scanner) {
    if (operator == '+')
        return evaluateEXP(scanner) + evaluateEXP(scanner);
    if (operator == '-')
        return evaluateEXP(scanner) - evaluateEXP(scanner);
    if (operator == '*')
        return evaluateEXP(scanner) * evaluateEXP(scanner);
    if (operator == '/')
        return evaluateEXP(scanner) / evaluateEXP(scanner);
    if (operator == '!')
        //if ! used then uses factorial method
        return factorial(evaluateEXP(scanner));
    //RunTimeException allows to return an error string in a int "type" method
    throw new RuntimeException("operator not allowed for this language");
}

3) Building up on the 2nd solution you could also merge the 2 methods as they are so closely linked anyways:

//input contains the expression user has entered
public static int evaluateEXP(Scanner scanner) {
    //checks if there is another digit after current one
    //allows expression to be looked at from right to left
    if (scanner.hasNextInt())
        return scanner.nextInt();

    //if there is another digit after current one then
    //operands and operators are established
    char operator = scanner.next().charAt(0);

    if (operator == '+')
        return evaluateEXP(scanner) + evaluateEXP(scanner);
    if (operator == '-')
        return evaluateEXP(scanner) - evaluateEXP(scanner);
    if (operator == '*')
        return evaluateEXP(scanner) * evaluateEXP(scanner);
    if (operator == '/')
        return evaluateEXP(scanner) / evaluateEXP(scanner);
    if (operator == '!')
        //if ! used then uses factorial method
        return factorial(evaluateEXP(scanner));
    //RunTimeException allows to return an error string in a int "type" method
    throw new RuntimeException("operator not allowed for this language");
}
Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
  • Been reading all of them over and over. Makes a lot of sense, even the demonstrators (student helpers) had no idea what was wrong with my code. Will definitely look into debuggers. Thanks for the help, sorry that I don't have enough rep to upvote. – jay park Oct 11 '18 at 21:18
  • @jaypark Glad I could help. You can still accept the answer :) – Max Vollmer Oct 11 '18 at 21:19
  • 1
    Yes I just realised. I pressed the check mark. Thank you! – jay park Oct 11 '18 at 21:22
0
public static int factorial(int n) {
    if(n==1){
        return 1;
    }
    // the next line is wrong.  I've removed a set of parentheses to make it more clear 
    int output =  factorial((n-1)*n);
    return output;
}

If would highly recommend running through your code in your head. If I pass 2 to your method what value is factorial() called with recursively? Hint: it isn't 1.

RedDeckWins
  • 2,111
  • 14
  • 16
  • my mistake, it should be else after return 1. I'll edit my question. I don't think the mistake is in the factorial method as the lecturer made it and suggested we used his. – jay park Oct 11 '18 at 19:41
  • @jaypark Adding `else` changes nothing. You get an endless loop with 2, because `(2-1)*2` is `2`. – Max Vollmer Oct 11 '18 at 19:49
  • @MaxVollmer Changed to the above code, still same error. – jay park Oct 11 '18 at 19:52
  • @jaypark That code **is** the error. RedDeckWins removed the brackets to make it more obvious. It is not the solution, it just shows you what's wrong. – Max Vollmer Oct 11 '18 at 19:53
  • @MaxVollmer edited the code again, changed the factorial method, this time should be right but still getting the same error – jay park Oct 11 '18 at 20:06
  • Maybe next time I will just give them the codez because trying to have someone who is clearly a student actually learn to reason about code isn't worth an upvote anymore – RedDeckWins Oct 11 '18 at 22:21
0
public static long fact(int n) {
    return n == 0 ? 1L : n * fact(n - 1);
}

It is better to use long as return type, because 17! is greater than Integer.MAX_VALUE

Oleg Cherednik
  • 17,377
  • 4
  • 21
  • 35