如何以正确的顺序执行乘法和/或除法?

时间:2019-01-31 14:49:34

标签: swift

我正在做一个简单的计算器,但是当执行乘法和除法运算时,我的代码并没有使它们优先于正负。 当执行-> 2 + 2 * 4时,结果= 16而不是10 ... 如何符合我的switch语句中的数学逻辑?

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
      case "÷":
        total /= number
      case "×":
        total *= number
      default:
        break
      }
    }
  }
  clear()
  return total
}

5 个答案:

答案 0 :(得分:1)

假设您想为任何算术表达式提供一种通用的,也许是可扩展的算法,那么正确的方法就是使用Shunting Yard algorithm

您有一个输入流,它是用户键入时所输入的数字和运算符,而您有一个输出流,它是相同的数字和运算符,但重新排列为反向波兰语表示法。因此,例如2 + 2 * 4将被转换为2 2 4 * +,通过在读取时将数字放在堆栈上,并在读取它们时将运算符应用于堆栈的顶部项,可以很容易地计算出它们。 / p>

要做到这一点,该算法具有一个运算符堆栈,该运算符堆栈可以可视化为壁板(因此称为“调车场”),低优先级的运算符被分流到其中,直到需要它们为止。

一般算法是

  • 从输入中读取项目
  • 如果是数字,请将其发送到输出
  • 如果该数字是运算符,则
    • 堆栈顶部的运算符的优先级高于您在堆栈上弹出该运算符并将其发送到输出的运算符的优先级
    • 将您从输入中读取的运算符推入堆栈
  • 重复以上操作,直到输入为空
  • 将堆栈上的所有运算符弹出到输出中

因此,如果您有2 + 2 * 4(注意,堆栈顶部在左侧,堆栈底部在右侧)

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:

我上面链接的Wikipedia条目具有更详细的描述以及一种可以处理括号和函数调用并且扩展性强得多的算法。


为完整起见,这是我简化版算法的实现

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())

答案 1 :(得分:0)

首先,您必须在数组中搜索以查看是否存在÷或×符号。

比起您可以相加或相减。

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
}

如果您不使用复数,这将起作用。

答案 2 :(得分:0)

如果只有四个操作+-x÷,则可以通过跟踪pendingOperand和{ pendingOperation每当遇到+-时。

然后在遇到另一个+-时或在计算结束时计算挂起操作。请注意,+-计算待处理的操作,但随后立即开始新的操作。

我修改了您的函数,将stringNumbersoperatorsinitial的值作为输入,以便可以在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
}

测试

// 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

答案 3 :(得分:0)

如果您不在乎速度,它是由计算机运行的,则可以使用机器方式来处理它。只需选择一个可行的计算即可,然后重复进行直到计算出每一个。

在这里很有趣。我使用一些愚蠢的变量和函数名称。

  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)

答案 4 :(得分:0)

使用JeremyP方法和Shunting Yard算法是对我有用的方法,但是我与操作员关联性(左或右优先级)存在一些差异,因此我必须使用它并开发了代码,它基于JeremyP答案,但使用数组。

首先,我们有了一个数组,其中的计算以字符串表示,例如:

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

我们使用下面的函数通过Shunting Yard算法获取RPN版本。

     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
}

,然后在下面的函数中输入RPN数组以计算结果。在每个循环中,我们删除之前使用的数字和操作数,然后导入前一个结果和数组中的两个“ p”,因此最后剩下解决方案和“ 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