How to execute multiplications and/or divisions in the right order?

后端 未结 5 1689
伪装坚强ぢ
伪装坚强ぢ 2020-12-21 21:11

I am doing a simple calculator, but when performing the multiplication and division, my code doesn\'t make them a priority over plus and minus. When doing -> 2 + 2 * 4, res

相关标签:
5条回答
  • 2020-12-21 21:25

    Using JeremyP method and the Shunting Yard algorithm was the way that worked for me, but I had some differences that had to do with the Operator Associativity(left or right priority) so I had to work with it and I developed the code, which is based on JeremyP answer but uses arrays.

    First we have the array with the calculation in Strings, e.g.:

     let testArray = ["10","+", "5", "*" , "4", "+" , "10", "+", "20", "/", "2"]
    

    We use the function below to get the RPN version using the Shunting Yard algorithm.

         func getRPNArray(calculationArray: [String]) -> [String]{
    
         let c = calculationArray
         var myRPNArray = [String]()
         var operandArray = [String]()
    
          for i in 0...c.count - 1 {
    
              if c[i] != "+" && c[i] != "-" && c[i] != "*" && c[i] != "/" {
                  //push number
                  let number = c[i]
                  myRPNArray.append(number)
    
              } else  {
                  //if this is the first operand put it on the opStack
                  if operandArray.count == 0 {
                      let firstOperand = c[i]
                      operandArray.append(firstOperand)
                  } else {
    
                      if  c[i] == "+" || c[i] == "-" {
    
                              operandArray.reverse()
                              myRPNArray.append(contentsOf: operandArray)
                              operandArray = []
    
                              let uniqOperand = c[i]
                              operandArray.append(uniqOperand)
    
    
    
                      } else if c[i] == "*" || c[i] == "/" {
    
                        let strongOperand = c[i]
    
                        //If I want my mult./div. from right(eg because of parenthesis) the line below is all I need
                        //--------------------------------
                        //      operandArray.append(strongOperand)
                       //----------------------------------
    
                        //If I want my mult./div. from left
                        let lastOperand = operandArray[operandArray.count - 1]
    
                        if lastOperand == "+" || lastOperand == "-" {
                            operandArray.append(strongOperand)
                        } else {
                            myRPNArray.append(lastOperand)
                            operandArray.removeLast()
                            operandArray.append(strongOperand)
    
                        }
                      }
                  }
              }
    
          }
    
        //when I have no more numbers I append the reversed operant array
          operandArray.reverse()
          myRPNArray.append(contentsOf: operandArray)
          operandArray = []
    
          print("RPN: \(myRPNArray)")
        return myRPNArray
    }
    

    and then we enter the RPN array in the function below to calculate the result. In every loop we remove the numbers and the operand used before and we import the previous result and two "p" in the array so in the end we are left with the solution and an array of "p".

      func getResultFromRPNarray(myArray: [String]) -> Double {
        var a = [String]()
        a = myArray
         print("a: \(a)")
        var result = Double()
        let n = a.count
    
    
    
        for i in 0...n - 1 {
            if n < 2 {
                result = Double(a[0])!
            } else {
                if a[i] == "p" {
                 //Do nothing else. Calculations are over and the result is in your hands!!!
                } else {
                    if a[i] == "+" {
    
                       result = Double(a[i-2])! + Double(a[i-1])!
                        a.insert(String(result), at: i-2)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.insert("p", at: 0)
                        a.insert("p", at: 0)
    
                    } else if a[i] == "-" {
                       result = Double(a[i-2])! - Double(a[i-1])!
                        a.insert(String(result), at: i-2)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.insert("p", at: 0)
                        a.insert("p", at: 0)
    
                    } else if a[i] == "*" {
                       result = Double(a[i-2])! * Double(a[i-1])!
                        a.insert(String(result), at: i-2)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.insert("p", at: 0)
                        a.insert("p", at: 0)
    
                    } else if a[i] == "/" {
                       result = Double(a[i-2])! / Double(a[i-1])!
                        a.insert(String(result), at: i-2)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.remove(at: i - 1)
                        a.insert("p", at: 0)
                        a.insert("p", at: 0)
                    } else {
                        // it is a number so do nothing and go the next one
                    }
    
    
                }//no over yet
            }//n>2
        }//iterating
        return result
    }//Func
    
    0 讨论(0)
  • 2020-12-21 21:32

    Assuming you want a generalised and perhaps extensible algorithm for any arithmetic expression, the right way to do this is to use the Shunting Yard algorithm.

    You have an input stream, which is the numbers and operators as the user typed them in and you have an output stream, which is the same numbers and operators but rearranged into reverse Polish notation. So, for example 2 + 2 * 4 would be transformed into 2 2 4 * + which is easily calculated by putting the numbers on a stack as you read them and applying the operators to the top items on the stack as you read them.

    To do this the algorithm has an operator stack which can be visualised as a siding (hence "shunting yard") into which low priority operators are shunted until they are needed.

    The general algorithm is

    • read an item from the input
    • if it is a number send it to the output
    • if the number is an operator then
      • while the operator on the top of the stack is of higher precedence than the operator you have pop the operator on the stack and send it to the output
      • push the operator you read from input onto the stack
    • repeat the above until the input is empty
    • pop all the operators on the stack into the output

    So if you have 2 + 2 * 4 (NB top of the stack is on the left, bottom of the stack is on the right)

    start:
        input: 2 + 2 * 4
        output: <empty>
        stack: <empty>
    
    step 1: send the 2 to output
        input: + 2 * 4
        output: 2
        stack: <empty>
    
    step 2: stack is empty so put + on the stack
        input: 2 * 4
        output: 2
        stack: +
    
    step 3: send the 2 to output
        input: * 4
        output: 2 2
        stack: +
    
    step 4: + is lower priority than * so just put * on the stack
        input: 4
        output: 2 2
        stack: * +
    
    step 5: Send 4 to output
        input:
        output: 2 2 4
        stack: * +
    
    step 6: Input is empty so pop the stack to output
        input:
        output: 2 2 4 * +
        stack:
    
    

    The Wikipedia entry I linked above has a more detailed description and an algorithm that can handle parentheses and function calls and is much more extensible.


    For completeness, here is an implementation of my simplified version of the algorithm

    enum Token: CustomStringConvertible
    {
        var description: String
        {
            switch self
            {
            case .number(let num):
                return "\(num)"
            case .op(let symbol):
                return "\(symbol)"
            }
        }
    
        case op(String)
        case number(Int)
    
        var precedence: Int
        {
            switch self
            {
            case .op(let symbol):
                return Token.precedences[symbol] ?? -1
            default:
                return -1
            }
        }
        var operation: (inout Stack<Int>) -> ()
        {
            switch self
            {
            case .op(let symbol):
                return Token.operations[symbol]!
            case .number(let value):
                return { $0.push(value) }
            }
        }
        static let precedences = [ "+" : 10, "-" : 10, "*" : 20, "/" : 20]
        static let operations: [String : (inout Stack<Int>) -> ()] =
        [
            "+" : { $0.push($0.pop() + $0.pop()) },
            "-" : { $0.push($0.pop() - $0.pop()) },
            "*" : { $0.push($0.pop() * $0.pop()) },
            "/" : { $0.push($0.pop() / $0.pop()) }
        ]
    }
    
    struct Stack<T>
    {
        var values: [T] = []
    
        var isEmpty: Bool { return values.isEmpty }
        mutating func push(_ n: T)
        {
            values.append(n)
        }
    
        mutating func pop() -> T
        {
            return values.removeLast()
        }
    
        func peek() -> T
        {
            return values.last!
        }
    }
    
    func shuntingYard(input: [Token]) -> [Token]
    {
        var operatorStack = Stack<Token>()
        var output: [Token] = []
    
        for token in input
        {
            switch token
            {
            case .number:
                output.append(token)
            case .op:
                while !operatorStack.isEmpty && operatorStack.peek().precedence >= token.precedence
                {
                    output.append(operatorStack.pop())
                }
                operatorStack.push(token)
            }
        }
        while !operatorStack.isEmpty
        {
            output.append(operatorStack.pop())
        }
        return output
    }
    
    let input: [Token] = [ .number(2), .op("+"), .number(2), .op("*"), .number(4)]
    let output = shuntingYard(input: input)
    
    print("\(output)")
    
    var dataStack = Stack<Int>()
    
    for token in output
    {
        token.operation(&dataStack)
    }
    
    print(dataStack.pop())
    
    0 讨论(0)
  • 2020-12-21 21:35

    If you only have the four operations +, -, x, and ÷, you can do this by keeping track of a pendingOperand and pendingOperation whenever you encounter a + or -.

    Then compute the pending operation when you encounter another + or -, or at the end of the calculation. Note that + or - computes the pending operation, but then immediately starts a new one.

    I have modified your function to take the stringNumbers, operators, and initial values as input so that it could be tested independently in a Playground.

    func calculateTotal(stringNumbers: [String], operators: [String], initial: Double) -> Double {
    
        func performPendingOperation(operand: Double, operation: String, total: Double) -> Double {
            switch operation {
            case "+":
                return operand + total
            case "-":
                return operand - total
            default:
                return total
            }
        }
    
        var total = initial
        var pendingOperand = 0.0
        var pendingOperation = ""
    
        for (i, stringNumber) in stringNumbers.enumerated() {
            if let number = Double(stringNumber) {
                switch operators[i] {
                case "+":
                    total = performPendingOperation(operand: pendingOperand, operation: pendingOperation, total: total)
                    pendingOperand = total
                    pendingOperation = "+"
                    total = number
                case "-":
                    total = performPendingOperation(operand: pendingOperand, operation: pendingOperation, total: total)
                    pendingOperand = total
                    pendingOperation = "-"
                    total = number
                case "÷":
                    total /= number
                case "×":
                    total *= number
                default:
                    break
                }
            }
        }
    
        // Perform final pending operation if needed
        total = performPendingOperation(operand: pendingOperand, operation: pendingOperation, total: total)
    
        // clear()
        return total
    }
    

    Tests:

    // 4 + 3    
    calculateTotal(stringNumbers: ["3"], operators: ["+"], initial: 4)
    
    7
    
    // 4 × 3
    calculateTotal(stringNumbers: ["3"], operators: ["×"], initial: 4)
    
    12
    
    // 2 + 2 × 4
    calculateTotal(stringNumbers: ["2", "4"], operators: ["+", "×"], initial: 2)
    
    10
    
    // 2 × 2 + 4
    calculateTotal(stringNumbers: ["2", "4"], operators: ["×", "+"], initial: 2)
    
    8
    
    // 17 - 2 × 3 + 10 + 7 ÷ 7
    calculateTotal(stringNumbers: ["2", "3", "10", "7", "7"], operators: ["-", "×", "+", "+", "÷"], initial: 17)
    
    22
    
    0 讨论(0)
  • 2020-12-21 21:35

    First you have to search in the array to see if there is a ÷ or × sign.

    Than you can just sum or subtract.

    mutating func calculateTotal() -> Double {
      var total: Double = 0
      for (i, stringNumber) in stringNumbers.enumerated() {
        if let number = Double(stringNumber) {
          switch operators[i] {
          case "÷":
            total /= number
          case "×":
            total *= number
          default:
            break
          }
          //Remove the number from the array and make another for loop with the sum and subtract operations.
    
        }
      }
      clear()
      return total
    }
    

    This will work if you are not using complex numbers.

    0 讨论(0)
  • 2020-12-21 21:36

    If you don't care speed, as it's running by a computer and you may use the machine way to handle it. Just pick one feasible calculate to do it and then repeat until every one is calculated.

    Just for fun here. I use some stupid variable and function names.

      func evaluate(_ values: [String]) -> String{
            switch values[1] {
            case "+": return String(Int(values[0])! + Int(values[2])!)
            case "-": return String(Int(values[0])! - Int(values[2])!)
            case "×": return String(Int(values[0])! * Int(values[2])!)
            case "÷": return String(Int(values[0])! / Int(values[2])!)
            default: break;
            }
            return "";
        }
    
        func oneTime(_  string: inout String, _ strings: [String]) throws{
        if let first = try NSRegularExpression(pattern:  "(\\d+)\\s*(\(strings.map{"\\\($0)"}.joined(separator: "|")))\\s*(\\d+)", options: []).firstMatch(in: string , options: [], range: NSMakeRange(0, string.count)) {
        let tempResult = evaluate((1...3).map{ (string as NSString).substring(with: first.range(at: $0))})
           string.replaceSubrange(  Range(first.range(at: 0), in: string)!  , with: tempResult)
            }
        }
    
        func recursive(_ string: inout String, _ strings: [String]) throws{
            var count : Int!
            repeat{ count = string.count ; try oneTime(&string, strings)
            } while (count != string.count)
        }
    
        func final(_ string: inout String, _ strings: [[String]])  throws -> String{
           return try strings.reduce(into: string) { (result, signs) in
                try recursive(&string, signs)
            }}
    
        var string = "17 - 23 + 10 + 7 ÷ 7"
        try final(&string, [["×","÷"],["+","-"]])
        print("result:" + string)
    
    0 讨论(0)
提交回复
热议问题