1

I would like some help with an app I'm programming which has to do with Arithmetic Progression. Basically the user inputs a formula - which contains a variable n - and the range of terms they want to calculate. Then the app should calculate every case, replacing n with the current term being calculated, starting from the first term until it reaches the last, and finally displaying the complete sequence. For example, if I input this as the formula:

An = n + 4

And in the range of terms:

from 1 to 5

The output should be:

A1 = 5.0  //because it is n + 4 and the current term n is 1, so 1 + 2
A2 = 6.0  //n + 4, n = 2 so 3 + 2, and so on...
A3 = 7.0  
A4 = 8.0
A5 = 9.0  //n reached 5, so the calculation stops there.
Complete sequence: ( 5.0 , 6.0 , 7.0 , 8.0 , 9.0 )

The result is defined as a double, as it won't always be an integer (e.g. An = n + 1/2).

In order to evaluate the math expression, I'm using @Boann's eval method, which is this one:

private static double eval(final String str) {
    return new Object() {
        int pos = -1, ch;

        void nextChar() {
            ch = (++pos < str.length()) ? str.charAt(pos) : -1;
        }

        boolean eat(int charToEat) {
            while (ch == ' ') nextChar();
            if (ch == charToEat) {
                nextChar();
                return true;
            }
            return false;
        }

        double parse() {
            nextChar();
            double x = parseExpression();
            if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char) ch);
            return x;
        }

        // Grammar:
        // expression = term | expression `+` term | expression `-` term
        // term = factor | term `*` factor | term `/` factor
        // factor = `+` factor | `-` factor | `(` expression `)`
        //        | number | functionName factor | factor `^` factor

        double parseExpression() {
            double x = parseTerm();
            for (; ; ) {
                if (eat('+')) x += parseTerm(); // addition
                else if (eat('-')) x -= parseTerm(); // subtraction
                else return x;
            }
        }

        double parseTerm() {
            double x = parseFactor();
            for (; ; ) {
                if (eat('*')) x *= parseFactor(); // multiplication
                else if (eat('/')) x /= parseFactor(); // division
                else return x;
            }
        }

        double parseFactor() {
            if (eat('+')) return parseFactor(); // unary plus
            if (eat('-')) return -parseFactor(); // unary minus

            double x;
            int startPos = this.pos;
            if (eat('(')) { // parentheses
                x = parseExpression();
                eat(')');
            } else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
                while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
                x = Double.parseDouble(str.substring(startPos, this.pos));
            } else if (ch >= 'a' && ch <= 'z') { // functions
                while (ch >= 'a' && ch <= 'z') nextChar();
                String func = str.substring(startPos, this.pos);
                x = parseFactor();
                if (func.equals("sqrt")) x = Math.sqrt(x);
                else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
                else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
                else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
                else throw new RuntimeException("Unknown function: " + func);
            } else {
                throw new RuntimeException("Unexpected: " + (char) ch);
            }

            if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation

            return x;
        }
    }.parse();
}

And in order to make this work (as the method above won't accept my custom variable n), I'm trying to use the replace() method to replace the character n with an integer i - representing the current term - which is defined in a for loop:

    for (i = firstTerm; i < lastTerm; ) {
    String strResult = strFormula.replace('n', (char) i); //the Result string, given after replacing the character "n" from the formula with the variable "i" so it can be evaluated

    termsAndResults.add("A" + i + " = " + eval(strResult) + "\n"); //add the current term and its result (e.g. "A3 = 8", "A4 = 10") to a list
    allResults.add(String.valueOf(eval(strResult))); //add the result to a list so each of them can be displayed later
    i++; //move onto the next term
    if (i == lastTerm) { 
        textResults.setText("  " + termsAndResults.toString().replace('[', ' ').replace(']', ' ').replace(',', ' ').trim() + "\nComplete sequence: (" + allResults.toString().replace('[', ' ').replace(']', ' ').trim() + ", ...)"); //display each term, its results and finally the complete sequence. All those replace and trim methods are there just so there won't be any "," or "[" and "]" when the results are displayed
    }

But the app just crashes whenever I input a formula containing "n". Logcat:

com.example.APTesting E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.APTesting, PID: 22158
java.lang.RuntimeException: Unexpected: [and a square character which doesn't show up here as text][2]
    at com.example.APTesting.MessageFragment$1.parseFactor(MessageFragment.java:96)
    at com.example.APTesting.MessageFragment$1.parseTerm(MessageFragment.java:66)
    at com.example.APTesting.MessageFragment$1.parseExpression(MessageFragment.java:57)
    at com.example.APTesting.MessageFragment$1.parse(MessageFragment.java:45)
    at com.example.APTesting.MessageFragment.eval(MessageFragment.java:103)
    at com.example.APTesting.MessageFragment.access$300(MessageFragment.java:20)
    at com.example.APTesting.MessageFragment$3.onClick(MessageFragment.java:149)
    at android.view.View.performClick(View.java:7161)
    at android.view.View.performClickInternal(View.java:7133)
    at android.view.View.access$3500(View.java:804)
    at android.view.View$PerformClick.run(View.java:27416)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:241)
    at android.app.ActivityThread.main(ActivityThread.java:7617)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)

This is the character that doesn't show up. By clicking on the error messages, the issue seems to come from termsAndResults.add("A" + i + " = " + eval(strResult) + "\n"), which probably happens due to replacing 'n' with i The error message comes from within the eval() method - throw new RuntimeException("Unexpected: " + (char) ch);. I tried the solutions from similar questions but pretty much the same error pops up. I'm a beginner. Any help would be appreciated.

2 Answers2

1

Casting those values of i to a char results in a non-printable character. For example, if i is 1, then the resulting character will become the start of header control character. That's the reason you see that box with a question mark in it.

You need the string value of the integer i. Just use

strFormula.replace("n", String.valueOf(i))
MC Emperor
  • 22,334
  • 15
  • 80
  • 130
0

Please try with this..

String strResult = strFormula.replace('n','i').trim().replace(" ","")
Jaimil Patel
  • 1,301
  • 6
  • 13
  • It still crashes. This will just replace the character 'n' in the string with the character 'i' (not with its value, just its character), and the eval() method can't evaluate the char i, so it doesn't work. Thank you anyway. `java.lang.RuntimeException: Unknown function: i` – relative_lay0ut Apr 17 '20 at 14:52