新建一个类,作为计算器的 Model 层,这里将类命名为 CalculatorBrain
import Foundation class CalculatorBrain { //1 private enum Op { case Operand(Double) case UnaryOperation(String,Double -> Double) case BinaryOperation(String,(Double,Double) -> Double) } //2 private var opStack = [Op]() private var knownOps = [String:Op]() //3 init() { knownOps["×"] = Op.BinaryOperation("×",*) knownOps["÷"] = Op.BinaryOperation("÷",{ $1 / $0 }) knownOps["+"] = Op.BinaryOperation("+",+) knownOps["−"] = Op.BinaryOperation("−",{ $1 - $0 }) knownOps["√"] = Op.UnaryOperation("√",sqrt) } //4 func evaluate(ops: [Op]) -> (result: Double?,remainOps: [Op]) { if !ops .isEmpty { let op = ops.removeLast() // error : Immutable value of type '[CalculatorBrain.Op]' only has mutating members named 'removeLast' } return (nil,ops) } //6 func pushOperand(operand: Double) { opStack.append(Op.Operand(operand)) } //7 func performOperation(symbol: String) { if let operation = knownOps[symbol] { opStack.append(operation) } } }东西很多,一点一点消化吧,
//1
定义了一个私有的 enum,一般在 object - C 里面我们是这样定义枚举的
typedef NS_ENUM(NSUInteger,HTTPMethod) { HTTPMethodGet,HTTPMethodPost,};但是这里的枚举值却是函数,很奇怪,这个还得去查资料了解一下,先放着.........
//2
声明了一个数组 opStack 和一个字典 knownOps 并初始化。
//3
函数的初始化,为 knownOps 字典赋值,这里比较有趣,BinaryOperation 在定义需要的参数是一个 String 和函数(这个函数需要 2 个 Double 类型的参数,返回值是 Double 类型),而在初始化时这样写 knownOps["÷"] = Op.BinaryOperation("÷",{ $1 / $0 }) 并不奇怪,swift 学习笔记1 里面已经说过了,有趣的是这里的闭包竟然可以直接省略成 * 和 + 就可以代表 (Double,Double) -> Double 了。这是因为在 swift 里所有的操作符都是符号,* 其实就是代表 { $0 * $1 } 了,+ 代表 { $0 + $1 } ,所以可以直接省略写成 * 和 + ,至于 ÷ 和 − 后面的闭包 { $1 / $0 } 和 { $1 - $0 } 不能省略成÷ 和 − ,因为运算方向是相反的。
//4
有两个知识点
1)函数返回的是一个元组(Tuple)
2)这里的 let op = ops.removeLast() 会报错,因为 ops 是一个不可变变量。为什么是一个不可变变量呢?swift 里规定,传过来的参数,除了类之外其他的都是值拷贝,类为引用。数组和字典都是结构体,所以是值拷贝,这点需要牢记。
当然,你可以这样修改:
var remainingOps = ops; let op = remainingOps.removeLast()
完整的 CalculatorBrain 代码如下:
// // CalculatorBrain.swift // Calculator // // Created by aaron.zheng on 2015-06-27. // Copyright © 2015 aaron.zheng. All rights reserved. // import Foundation class CalculatorBrain { //1 private enum Op: CustomStringConvertible { case Operand(Double) case UnaryOperation(String,Double) -> Double) var description: String { //要实现自己的 description 就得让 enum 实现 CustomStringConvertible 协议。 get { switch self { case .Operand(let operand): return "\(operand)" case .UnaryOperation(let symbol,_): return symbol case .BinaryOperation(let symbol,_): return symbol } } } } //2 private var opStack = [Op]() private var knownOps = [String:Op]() //3 init() { func learnOp(op: Op) { knownOps[op.description] = op } learnOp(Op.BinaryOperation("×",*)) //下面的同样可以换成用 learnOp。 knownOps["÷"] = Op.BinaryOperation("÷",sqrt) } //4 private func evaluate(ops: [Op]) -> (result: Double?,remainOps: [Op]) { if !ops .isEmpty { var remainingOps = ops; let op = remainingOps.removeLast() switch op { case .Operand(let operand): return (operand,remainingOps) case .UnaryOperation(_,let operation): let operandEvaluation = evaluate(remainingOps) if let operand = operandEvaluation.result { return (operation(operand),operandEvaluation.remainOps) } case .BinaryOperation(_,let operation): let op1Evaluation = evaluate(remainingOps) if let operand1 = op1Evaluation.result { let op2Evaluation = evaluate(op1Evaluation.remainOps) if let operand2 = op2Evaluation.result { return (operation(operand1,operand2),op2Evaluation.remainOps) } } } } return (nil,ops) } //5 func evaluate() -> Double? { let (result,_) = evaluate(opStack) //_ 起占位作用,表示我不关心该参数。 return result } //6 func pushOperand(operand: Double) -> Double? { opStack.append(Op.Operand(operand)) return evaluate() } //7 func performOperation(symbol: String) -> Double? { if let operation = knownOps[symbol] { opStack.append(operation) } return evaluate() } }更多可以看《Developing IOS 8 App With Swift》的 Applying MVC 一节。