1

I've made a graphical calculator app using the Processing Development Environment and have incorporated a Java Operation class. Then I called instances of the Operation class in my main file - calculator.pde. When I call the method on the multiple instances, conditionally, it prints output from the method multiple times because of the loop and conditional from which the method is called.

Here's the calculate() method of Operation class:

String calculate() {
    int operationIndex = userInput.indexOf(operationSymbol);
    ArrayList<String> clickedNumbers = new ArrayList<String>();
    System.out.println(operation + " index: " + operationIndex);
    for (int i = 0; i < operationIndex; i++) {
      clickedNumbers.add(userInput.get(i));         }
    double double1 = calculator.stringToDouble(String.join("", clickedNumbers)); 
    System.out.println("double1: " + double1);
    clickedNumbers.clear();
    int equalsIndex = userInput.indexOf("=");
    for (int i = operationIndex + 1; i < equalsIndex; i++) {
      clickedNumbers.add(userInput.get(i));
    }
    double double2 = calculator.stringToDouble(String.join("", clickedNumbers)); 
    System.out.println("double2: " + double2);
    Double operResult = this.doOperation(double1, double2);
    String operationResult = calculator.doubleToString(operResult);
    System.out.println("Operation Result: " + operationResult);
    return operationResult;
}

All System.out.println() statements depend on local variables inside the method. These are what should be printing out to the console only once. Whenever the user puts in an operation and presses equals, for example, if s/he inputs 15 * 3, it outputs:

Output of Console with desired output highlighted

Above, the highlighted part of the console output is my desired output.

Here is my code where the calculate() method is called:

  String[] numberStr = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
  Boolean pressedEquals = listName.contains("=");

  for(String number : numberStr) {
  Boolean pressedNumber = listName.contains(number);
    if (pressedNumber && pressedEquals) {
      if (listName.contains("+")) {
        processStatement = "Adding Numbers...";
        result = addition.calculate();
      }
      else if (listName.contains("-")) {
        processStatement = "Subtracting Numbers...";
        result = subtraction.calculate();
      }
      else if (listName.contains("*")) {
        processStatement = "Multiplying Numbers...";
        result = multiplication.calculate();
      }
      else if (listName.contains("/")) {
        processStatement = "Dividing Numbers...";
        result = division.calculate();
      }
      else if (listName.contains("%")) {
        processStatement = "Modulufying Numbers...";
        result = modulus.calculate();
      }
      else if (listName.contains("^")) {
        processStatement = "Expounding numbers...";
        result = exponential.calculate();
      }
    } 
  }

I don't understand why it is printing output as many times as the length of the userInput ArrayList. I know the problem is the pressedNumber Boolean in the for loop. I know the OP on this question had the same problem, with it the printing a number of times depending on a length of user input, but the answer to the question did not explain why it was doing this.

Research and Trials that didn't work

Part of fixing this was making the processStatement into a variable because earlier I just printed it inside the condition. This didn't work because it printed multiple times. I cannot do this for the println statements inside the method because they depend on the variables inside the method, and there are quite a few statements. My second plan was to make a static method printInfo(), but this also wouldn't have worked because of the variables being too tightly scoped, and I can't define them outside because then that would be inaccurate.

Update

I have looked more on stack overflow, this time for regular expressions, and these questions added to my research to solve this problem:

Marvin
  • 853
  • 2
  • 14
  • 38
  • Well, from a pure Java perspective, it makes no sense to print something once if you're called inside a loop. The solution is to not print anything at all and have the calling code, not the function, do the printing. – markspace Aug 24 '18 at 01:38
  • The better question to ask is "Why is the function being called multiple times in a loop?" – Kevin Anderson Aug 24 '18 at 01:48
  • @markspace Can you explain more in an answer? – Marvin Aug 24 '18 at 02:29
  • @markspace It is supposed to only print once because the print statement is called under a condition in a loop. The condition contains the counter for the loop (`number`). – Marvin Aug 24 '18 at 02:39
  • By the way, I strongly recommend to follow the Java Naming Conventions: variable names and methods always start with lowercase. So `ClickedNumbers` should be `clickedNumbers` and `StringToDouble` should be `stringToDouble`. – MC Emperor Aug 24 '18 at 05:42
  • @MCEmperor okay, I will change that. Thank you. – Marvin Aug 24 '18 at 13:44

1 Answers1

1

Here are a few things that you might want to reconsider in your code:

For example, take a look at this snippet:

String[] numberStr = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
Boolean pressedEquals = listName.contains("=");

for(String number : numberStr) {
    Boolean pressedNumber = listName.contains(number);
    // ...
}

You don't need this loop here. You can simply validate the listName that it contains digits, operators or not i.e. it is a valid expression. And, then simply call the calculate() method if it is valid. You may also use regex-based validation to enforce your particular expression format.

Skipping that loop and after performing validation, it would look like this:

// Validate expression format here
// Example: <number> <operator> <number> <=>
//             1          *        5      =

final boolean isValidated = isValidExpression( /* expression */ );

if ( isValidated == true )
{
    final String op = /* get operator here */;
    switch ( op )
    {
        case "+":
            result = addition.calculate();
            break;

        case "-":
            result = subtraction.calculate();
            break;

       // ... /, *, % ...

        default:
            // ERROR: Invalid operator...
    }
}

Apart from this, you may use stack-based expression evaluation too.


UPDATE:

Here's an example of isValidExpression() method with regex:

// Test to validate expression with regex
// Example: <number> <operator> <number> <=>
//             1          *        5      =

import java.util.regex.Matcher;
import java.util.regex.Pattern;

class ExpressionValidator
{
    public static boolean isValidExpression( final String exp )
    {
        final String regex = "\\d+\\s*[*|/|+|-]\\s*\\d+\\s*[=]";
        final Pattern pattern = Pattern.compile( regex );
        final Matcher matcher = pattern.matcher( exp.trim() );
        return matcher.find();
    }

    public static void main( final String[] args )
    {
        final String[] expressions = 
        {  
            " 1 +  2 =",
            " 3 *  5 =",
            "12 + 10 =",
            " 33 = 25 ",
            " +65  65 ",
            "45 666  ="
        };

        for ( final String exp : expressions )
        {
            System.out.println( "[" + exp + "] >> " + isValidExpression( exp ) );
        }
    }
}

Output:

[ 1 +  2 =] >> true
[ 3 *  5 =] >> true
[12 + 10 =] >> true
[ 33 = 25 ] >> false
[ +65  65 ] >> false
[45 666  =] >> false

Here's the live example: https://ideone.com/S9Wf9b

Azeem
  • 11,148
  • 4
  • 27
  • 40
  • Thanks for the answer Azeem. I looked it up and it seems validateExpression is not an actual method, so, would I have to make my own function for this? If it is, would I just type `return ` `? Also, I would not use `final String op` in here because I already have it in my `calculate` method. – Marvin Aug 24 '18 at 13:54
  • Error: **calculator.pde:115:0:115:0: The function validateExpression(String) does not exist**. I put in the following code `String listString = String.join(", ", listName); final boolean isValidated = validateExpression(listString); if (isValidated == true) { if (listName.contains("+")) {` – Marvin Aug 24 '18 at 14:35
  • @Marvin : That was just an idea. You have to write your own validation function. You can use regular expression of Java. – Azeem Aug 24 '18 at 17:20
  • can you explain in your answer how to use regular expression since I have never used them before. Or point me to a link online. I couldn't find any tutorials on RegEx syntax. Thanks – Marvin Aug 24 '18 at 19:32
  • @Marvin: I've added an example to validate the input expression using regex. Hope that helps! – Azeem Aug 25 '18 at 04:43
  • Thank you very much! This helps! However, the user needs to be able to put in as many decimals as they want. Is there a way to make all decimals digits optional including the '.'? – Marvin Aug 25 '18 at 13:42
  • @Marvin : You are welcome! Honestly, using regex for such parsing problems is not the optimal way. You ought to consider writing your own parser. I believe you'll find some good ones online too. For this small trivial problem, it is okay though I would separate the regex compile part in the static block for optimization. – Azeem Aug 25 '18 at 16:36
  • `public static boolean validateExpression(String exp ) { // Reg Ex: - or + one or more numbers . oe or more numbers operator same thing String regex = "([-+]?[0-9]*\.?[0-9]+[\/\+\-\*])+([-+]?[0-9]*\.?[0-9]+)"; Pattern pattern = Pattern.compile( regex ); Matcher matcher = pattern.matcher( exp.trim() ); return matcher.find(); }` – Marvin Aug 25 '18 at 22:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178761/discussion-between-marvin-and-azeem). – Marvin Aug 25 '18 at 22:01