CS170 - Project 6

CS170, Project 6


Due: See class webpage

1. Purpose

The purpose of this project is to

Contrary to the previous project, the problem solving procedure used in this project is very "elegant" and not easy to develop. (You don't have to develop it, just implement it using the details I provide you). This algorithm was discovered by computer scientists and is now part of the computer science theory - which you will learn more in later courses in the CS major. You only have to implement the method that I will explain in details later in this handout. Just translate the instructions into a program. This happens a lot in real life where users want something done by the computer. You have to listen to the users (that you are supporting) and implement what they want.

2. Project Description

3. Help file

I have written a "driver" program that uses the ExpressionEval class. This program (pj6.java) can be obtained here: click here. Take a look at the program pj6.java. It creates an ExpressionEval object and then repeatedly read lines (which will be "expression strings") from the input and calls ExpressionEval's eval() method to evaluate the expression and print the result. Save a copy of this program in your project directory. After you have written the ExpressionEval class (in a separate file), you can compile the project with:
    javac pj6.java
and run it with:
    java pj6

Sample outputs (the inputs entered by the user are in red):

cheung@labss6g(2471)> java pj6
Enter expression>> 1 - 2 / 9
Answer>> 0.7777777777777778

Enter expression>> 3.9 + 4.5 / 6.7
Answer>> 4.571641791044776

Enter expression>> 1 + 1 - 1 / 2 * 3 - 4
Answer>> -3.5

4. Algorithm

4.1 Variables used in algorithm

The first thing you need to do in eval() is to StringTokenizer the input string. Let tok be the StringTokenizer containing the tokenized input string. (So if the input string is "1 + 2 / 9", then tok will have 5 tokens: "1", "+", "2", "/" and "9"). You will need 2 arrays and 2 index variables to implement the algorithm that I will describe later. First, let me give you the arrays and how they are to be used:
     double value[] = new double[4];
     int Nvalues;
     String operator[] = new String[4];
     int Noperators;

     Symbolically:

        value:          operator:
      +---------+      +---------+
    0 |         |    0 |         |         (The number at the left
      +---------+      +---------+	    of the array elements
    1 |         |    1 |         |	    is the array index)
      +---------+      +---------+
    2 |         |    2 |         |
      +---------+      +---------+
    3 |         |    3 |         |
      +---------+      +---------+

      The variables "Nvalues" indicates the number of values currently
      in the "value" array.

      The variables "Noperator" indicates the number of operators
      currently in the "operator" array.
Each array element of the value array contains a number. This number can be:

Each array element of the operator array contains an operator from the input string.

4.2 Operations on the value and operator array variables

The algorithm is best understood using the following 3 "high level" operations on the variables given in Section 4.2. Here are the operations:

  1. EnterValue(double x): enters the double value x into the value array.
       Example 1: EnterValue(9.0)
    
          BEFORE                            AFTER: EnterValue(9.0)
          ======				======================
            value:       operator:           value:       operator:
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		| 9.0     |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
    
          Nvalues = 0   Noperators = 0	Nvalues = 1   Noperators = 0
    
       Example 2: EnterValue(1.4)
    
          BEFORE                            AFTER: EnterValue(1.4)
          ======				======================
            value:       operator:           value:       operator:
          +---------+   +---------+		+---------+   +---------+
          |  9.0    |   |         |		| 9.0     |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		| 1.4     |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
    
          Nvalues = 1   Noperators = 0	Nvalues = 2   Noperators = 0
    
  2. EnterOperation(String a): enters the String (containing an operation) a into the operation array.
       Example 1: EnterOperation("+")
    
          BEFORE                            AFTER: EnterOperation("+")
          ======				======================
            value:       operator:           value:       operator:
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |  "+"    |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
    
          Nvalues = 0   Noperators = 0	Nvalues = 0   Noperators = 1
    
       Example 1: EnterOperation("-")
    
          BEFORE                            AFTER: EnterOperation("-")
          ======				======================
            value:       operator:           value:       operator:
          +---------+   +---------+		+---------+   +---------+
          |         |   |  "+"    |		|         |   |  "+"    |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |  "-"    |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
    
          Nvalues = 0   Noperators = 1	Nvalues = 0   Noperators = 2
    
    
  3. Reduce():

    1. remove the last operation entered into the operation array,
    2. remove the last two values entered into the value array,
    3. perform the operation with the two values
    4. enter the result back into the value array.

    NOTE: Reduce() can only be done when the operation array is not empty !!!

       Example 1: 
    
          BEFORE                            AFTER: Reduce()
          ======				======================
            value:       operator:           value:       operator:
          +---------+   +---------+		+---------+   +---------+
          |  2      |   |   "+"   |		|   5     |   |         |
          +---------+   +---------+		+---------+   +---------+
          |  3      |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
    
          Nvalues = 2   Noperators = 1	Nvalues = 1   Noperators = 0
    
       **** Notice that BOTH Nvalues and Noperators are updated !!!
    
       Example 2:
    
          BEFORE                            AFTER: Reduce()
          ======				======================
            value:       operator:           value:       operator:
          +---------+   +---------+		+---------+   +---------+
          |    4    |   |  "+"    |		|    4    |   |  "+"    |
          +---------+   +---------+		+---------+   +---------+
          |    5    |   |  "-"    |		|   -2    |   |         |
          +---------+   +---------+		+---------+   +---------+
          |    7    |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
          |         |   |         |		|         |   |         |
          +---------+   +---------+		+---------+   +---------+
    
          Nvalues = 3   Noperators = 2	Nvalues = 2   Noperators = 1
    

4.3. The algorithm to evaluate an expression

First you have to understand what the problem is when you try to evaluate an expression by looking at each "token" from left to right: you may not be able to just compute the expression as you get the tokens one by one. Consider the following example. Suppose you get 3 tokens and they are: "1.2", "+" and "2.4". There are more tokens that remain to be read, but just consider what you can do right now. From these 3 tokens, you will conclude that the first part of input string is "1.2 + 2.4 ....". Can we now replace this by "3.6 + ...." (by adding 1.2 and 2.4) ? The answer is no: suppose the remaining input is: "1.2 + 2.4 * 2", then the multiple operation must be done before the "+" operation. The value array and the operator array will help you retain the things read until it is time to process the operations.

Let me give you the algorithm and then give you a few examples that shows its operation. The algorithm that you must implement is as follows:


	while ( tok.hasMoreTokens() )
	{
	   nextToken = tok.nextToken();

	   if (nextToken is "*" or "/")              // Case 1
	      enterOperation(nextToken);
	   else if (nextToken is "+" or "-")         // Case 2
	   {
	      if ( Noperations > 0 )
		 Reduce();
	      enterOperation(nextToken);
	   }
	   else // nextToken is a number....	     // Case 3
	   {
	       enterValue(Double.parseDouble(nextToken));

	       if ( Noperations > 0 AND
		    ( operation[Noperations-1] is "*" or "/") )
	          Reduce(); 
	   }
	}

        if ( Noperations > 0 )
	   Reduce();

	return( value[0] );

The algorithm will make the following step if the input is "1 + 6 / 2" (tokens: "1", "+", "6", "/" and "2")
Initially:

                           value:       operator: 
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 0   Noperators = 0

(1) Processing nextToken = "1": Case 3, nextToken is a number

	steps executed: (1) enterValue(Double.parseDouble(nextToken))
			(2) skip Reduce() because Noperations == 0

    The result: (1 is entered in the value array)

                           value:       operator: 
                         +---------+   +---------+
                         |    1    |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 1   Noperators = 0

(2) Processing nextToken = "+": Case 2

	steps executed: (1) it skips Reduce() because Noperations == 0
			(2) enterOperation(nextToken);

    The result: ("+" is entered in the operation array)

                           value:       operator: 
                         +---------+   +---------+
                         |    1    |   |   "+"   |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 1   Noperators = 1

(3) Processing nextToken = "6": Case 3, nextToken is a number

	steps executed: (1) enterValue(Double.parseDouble(nextToken))
			(2) skip Reduce() because the last operation
			    entered is a "+" which is not "*" or "/"

    The result: (6 is entered in the value array)

                           value:       operator: 
                         +---------+   +---------+
                         |    1    |   |   "+"   |
                         +---------+   +---------+
                         |    6    |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 2   Noperators = 1

(4) Processing nextToken = "/": Case 1

	steps executed: (1) enterOperation(nextToken);

    The result: ("/" is entered in the operation array)

                           value:       operator: 
                         +---------+   +---------+
                         |    1    |   |   "+"   |
                         +---------+   +---------+
                         |    6    |   |   "/"   |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 2   Noperators = 2

(5) Processing nextToken = "2": Case 3 (number)

	steps executed: (1) enterValue(Double.parseDouble(nextToken));
			    After enterValue, the array look like this:

                                 value:       operator: 
                               +---------+   +---------+
                               |    1    |   |   "+"   |
                               +---------+   +---------+
                               |    6    |   |   "/"   |
                               +---------+   +---------+
                               |    2    |   |         |
                               +---------+   +---------+
                               |         |   |         |
                               +---------+   +---------+

                               Nvalues = 3   Noperators = 2


			(2) Reduce() is executed because Noperations > 0
			    and operation[Noperations-1] is "/" !

			    The Reduce() operation remove 6 and 2
			    from the value array and remove
			    the "/" operation from the operation array and
			    compute "6 / 2", producing 3 which it then
			    enter back into the value array.

    The total result: ("6 / 2" is entered into the operation array)

                           value:       operator: 
                         +---------+   +---------+
                         |    1    |   |   "+"   |
                         +---------+   +---------+
                         |    3    |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 2   Noperators = 1

(6) No more token, while loop ends and the program executes:

			(1) Reduce() because Noperations > 0

			The Reduce() operation remove 1 and 3
                        from the value array and remove
                        the "+" operation from the operation array and
                        compute "1 + 3", producing 4 which it then
                        enter back into the value array.

    The result is:

                           value:       operator: 
                         +---------+   +---------+
                         |    4    |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+
                         |         |   |         |
                         +---------+   +---------+

                         Nvalues = 1   Noperators = 0


    Finally, value[0] (which is 4) is returned.
    And it is also equal to "1 + 6 / 2" !!!

5. Bonus Points

After you completed the project and you can obtain 30 extra bonus points by improving your program to handle bracketed expressions. For example:
   ( 1 + 3 ) * 2     outputs   8.0
   ( 1 + 3 )         outputs   4.0
   ( 1 )             outputs   1.0

6. Turn in