新建一个类,作为计算器的 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)
case BinaryOperation(String, (Double, 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("÷", { $1 / $0 })
knownOps["+"] = Op.BinaryOperation("+", +)
knownOps["−"] = Op.BinaryOperation("−", { $1 - $0 })
knownOps["√"] = Op.UnaryOperation("√", 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 一节。