簡介
斯坦福cs193p 2015年的冬季iOS8課程已經(jīng)陸續(xù)上線了。說實話當我看到它上線的時候還是挺激動的台汇,畢竟我也是從iOS6看到iOS7,每次都從那位 Paul Hegarty 做的demo中學到不少知識(哪怕抄一篇也是練手捌蚯伞)洋访。如果說喬布斯的本事是將復雜精密的電腦科技包裝成一個簡單美觀且易用的iPhone并介紹給大眾消費者的話,那么Paul老爺子的本事則是把復雜的iOS app開發(fā)通過他的課程和demo講解的淺顯易懂斑司,幫助任何愿意成為iOS開發(fā)者的朋友能更好的熟悉開發(fā)和上手渗饮。引用他的話就是:一次demo演示勝過千言萬語。但是前提是:首先要自己熟悉面向?qū)ο蟮母拍睿煜ゎ惡屠^承等等互站,否則越往后聽就會越跟不上私蕾。
今年的iOS課程最大的亮點就是swift,就目前發(fā)布的視頻來看胡桃,感覺斯坦福cs193p從今年起已經(jīng)不再該課程中過多的介紹objective c了踩叭,所有的demo全部體現(xiàn)的是嶄新的語言和思維。作為cs193p的粉絲翠胰,我也在這里發(fā)布自己的課后作業(yè)和解析容贝,如有任何疑問或反饋,歡迎溝通之景。(相比較德國的rwth aachen大學斤富,在今年推出的iOS8開發(fā)課程中僅有兩節(jié)swift視頻,如果有興趣了解以objective c為基礎(chǔ)的cocoa touch的朋友锻狗,可以從iTunes U下載他們的課程满力,很有幫助。)
概述
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var display: UILabel!
// 用戶是不是已經(jīng)輸入
var userIsInTheMiddleOfTypingANumber:Bool = false
@IBAction func appendDigit(sender: UIButton) {
// Swift是非常強類型的語言
// Swift有一個非常強大的特性,叫做類型推導
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
// 可選值(Optional)是不可以拼接字符串的,所以在這個地方,需要進行解包
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
print("digit = \(digit)")
}
}
Tips
- let用來定義常量(const)屋谭,常量定義之后就不可以改變脚囊。一定要初始化,不初始化會報錯桐磁。
- var用來定義變量悔耘,一定要初始化,不初始化會報錯我擂。
- swift語言有類型推導衬以,定義的變量或者常量的類型通過右邊的值來決定。
- ()用來在字符串中包含 相應的變量或者常量
- optional Type:這種類型的值有兩種狀態(tài)校摩,一種是無值(nil)看峻,一種是有值。
也就是衙吩,Optional類型允許變量沒有值互妓,其它類型如果沒有初始化值在使用時會報錯 。
optional 在有值得時候坤塞,值的類型是由 冯勉?左邊的值得類型來決定∧≤剑可以通過灼狰!來解包取得相應的類型。
optional類型的值不可以用于右值浮禾!- 按住option鍵點擊相應的變量交胚,來查看文檔份汗。
完成計算器,初探MVC設(shè)計模式
回車鍵及相關(guān)的代碼
var operandStack = [Double]()
@IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
operandStack.append(displayValue)
print("operandStack = \(operandStack)")
}
- 這個變量是一個內(nèi)部的棧,去存儲你輸入的這些數(shù),他的類型是一個數(shù)組,這個數(shù)組里存放的是Double的變量,注意在這個地方要初始化.(我們不能使用沒有經(jīng)過初始化的,會報錯)
- var operandStack: Array<Double> = Array<Double>()
var operandStack = Array<Double>()
// 用來進棧的數(shù)據(jù)
var displayValue:Double {
get {
// 將字符串轉(zhuǎn)換為double
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set {
// 將value轉(zhuǎn)換成字符串
display.text = "\(newValue)"
// 設(shè)置重新開始輸入字符串
userIsInTheMiddleOfTypingANumber = false
}
}
其中NSNumberFormatter類中比較常用的兩個方法:
- func stringFromNumber(number:NSNumber) -> String?
NSNumber 轉(zhuǎn)換成String - func numberFromString(string:String) -> NSNumber?
String轉(zhuǎn)換成 NSNumber
NSNumberFormatter().numberFromString(display.text!)!.doubleValue將字符串轉(zhuǎn)換為NSnumber蝴簇,然后將NSNumber轉(zhuǎn)換為Double杯活。
NSNumberFormatter()是用來初始化實例用的,這里是隱式军熏,表示用對象來引用其中的方法轩猩。
加減乘除開根號運算
v0.7
@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber
{
enter()
}
switch operation {
case "×":
if operandStack.count >= 2 {
displayValue = operandStack.removeLast() * operandStack.removeLast()
enter()
}
case "÷":
break
case "+":
break
case "?":
break
default:
break
}
}
v0.8
@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber
{
enter()
}
switch operation {
case "×": performOperation(multiply)
case "÷":
break
case "+":
break
case "?":
break
default:
break
}
}
// 定義一個方法用來進行加減乘除運算,參數(shù)類型是一個方法:(Double, Double)->Double
func performOperation(operation:(Double,Double) -> Double) {
if operandStack.count >= 2 { // 棧中必須有兩個元素才能進行加減乘除的運算
// 把最后的兩個元素分別出棧,然后進行運算
displayValue = operandStack.removeLast() * operandStack.removeLast()
enter()
}
}
func multiply(op1:Double, op2:Double) -> Double {
return op1 * op2
}
- Swift中可以將一個方法作為另一個方法的參數(shù):
performOperation(multiply)是將multiply方法作為performOperation方法的參數(shù)
v0.9
@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber
{
enter()
}
switch operation {
case "×": performOperation({ (op1:Double, op2:Double) -> Double in
return op1 * op2
})
case "÷":
break
case "+":
break
case "?":
break
default:
break
}
}
// 定義一個方法用來進行加減乘除運算,參數(shù)類型是一個方法:(Double, Double)->Double
func performOperation(operation:(Double,Double) -> Double) {
if operandStack.count >= 2 { // 棧中必須有兩個元素才能進行加減乘除的運算
// 把最后的兩個元素分別出棧,然后進行運算
displayValue = operandStack.removeLast() * operandStack.removeLast()
enter()
}
}
- Swift中可以隱式的將一個方法作為另一個方法的參數(shù)
case "×": performOperation({ (op1:Double, op2:Double) -> Double in
return op1 * op2
})
可以看到隱式方法作為參數(shù)的時候,要放在{}里面荡澎,稱之為閉包
- Swift有類型自動推導的功能均践,所以可以簡寫為:
case "×": performOperation({ (op1,op2) in
return op1 * op2
})
performOperation({ (op1,op2) in return op1 * op2 })
performOperation({ (op1,op2) in op1 * op2 })
- Swift中編譯器會自動給方法中的參數(shù)傳一個默認值,第一個參數(shù)$0,第二個參數(shù)$1........
所以可以繼續(xù)簡化為:
performOperation({ $0 * $1 })
- Swift中如果一個方法是另外一個方法的最后一個參數(shù),那么可以將此方法放到原方法后面:
performOperation() { $0 * $1 }
- 方法中無參數(shù)了可以直接去掉()
performOperation { $0 * $1 }
@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber
{
enter()
}
switch operation {
case "×": performOperation { $0 * $1 }
case "÷": performOperation { $1 / $0 } // 除數(shù)是棧中最先彈出的元素
case "+": performOperation { $0 + $1 }
case "?": performOperation { $1 - $0 }
default: break
}
}
// 定義一個方法用來進行加減乘除運算,參數(shù)類型是一個方法:(Double, Double)->Double
func performOperation(operation:(Double,Double) -> Double) {
if operandStack.count >= 2 { // 棧中必須有兩個元素才能進行加減乘除的運算
// 把最后的兩個元素分別出棧,然后進行運算
displayValue = operation(operandStack.removeLast(),operandStack.removeLast())
enter()
}
}
v1.0
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var display: UILabel!
var userIsInTheMiddleOfTypingANumber:Bool = false
@IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
print("digit = \(digit)")
}
@IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber
{
enter()
}
switch operation {
case "×": performOperation { $0 * $1 }
case "÷": performOperation { $1 / $0 }
case "+": performOperation { $0 + $1 }
case "?": performOperation { $1 - $0 }
case "√": performOperation { sqrt($0)}
default: break
}
}
// 定義一個方法用來進行加減乘除運算,參數(shù)類型是一個方法:(Double, Double)->Double
func performOperation(operation:(Double,Double) -> Double) {
if operandStack.count >= 2 { // 棧中必須有兩個元素才能進行加減乘除的運算
// 把最后的兩個元素分別出棧,然后進行運算
displayValue = operation(operandStack.removeLast(),operandStack.removeLast())
enter()
}
}
// 添加private,不讓Swift編譯器將其暴露給Objective-C運行時
private func performOperation(operation:Double -> Double) {
// 棧中必須多于一個元素才能進行開平方
if operandStack.count >= 1 {
displayValue = operation(operandStack.removeLast())
enter()
}
}
var operandStack = [Double]()
@IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
operandStack.append(displayValue)
print("operandStack = \(operandStack)")
}
// 用來進棧的數(shù)據(jù)
var displayValue:Double {
get {
// 將字符串轉(zhuǎn)換為double
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set {
// 將value轉(zhuǎn)換成字符串
display.text = "\(newValue)"
// 設(shè)置重新開始輸入字符串
userIsInTheMiddleOfTypingANumber = false
}
}
}
MVC設(shè)計模式初探
計算器的Model層
定義了一個私有的enum
private enum Op {
case Operand(Double) // 操作數(shù)
case UnaryOperation(String,Double -> Double) // 一元運算
case BinaryOperation(String,(Double,Double) ->Double) // 二元運算
}
聲明了一個數(shù)組opStack和一個字典knownOps并初始化.
private var opStack = [Op]()
private var knownOps = [String:Op]()
var knownOps = Dictionary<String,Op>()
簡寫為
private var knownOps = [String:Op]()
函數(shù)的初始化
init() {
knownOps["×"] = Op.BinaryOperation("×", *)
knownOps["÷"] = Op.BinaryOperation("÷"){ $1 / $0}
knownOps["+"] = Op.BinaryOperation("+", +)
knownOps["?"] = Op.BinaryOperation("?"){ $1 - $0}
knownOps["√"] = Op.UnaryOperation("√",sqrt)
}
函數(shù)的初始化,為 knownOps 字典賦值,這里比較有趣,BinaryOperation 在定義需要的參數(shù)是一個 String 和函數(shù)(這個函數(shù)需要 2 個 Double 類型的參數(shù)摩幔,返回值是 Double 類型), 而在初始化時這樣寫 knownOps["÷"] = Op.BinaryOperation("÷", { $1 / $0 }) 并不奇怪,有趣的是這里的閉包竟然可以直接省略成 * 和 + 就可以代表 (Double, Double) -> Double 了.這是因為在 Swift 里所有的操作符都是符號,* 其實就是代表 { $0 * $1 } 了,+ 代表 { $0 + $1 } ,所以可以直接省略寫成 * 和 + ,至于 ÷ 和 ? 后面的閉包 { $1 / $0 } 和 { $1 - $0 } 不能省略成 ÷ 和 ? ,因為運算方向是相反的彤委。
knownOps["×"] = Op.BinaryOperation("×"){ $0 * $1} 簡寫為
knownOps["×"] = Op.BinaryOperation("×", *)
knownOps["+"] = Op.BinaryOperation("+"){ $0 + $1} 簡寫為
knownOps["+"] = Op.BinaryOperation("+", +)
knownOps["√"] = Op.UnaryOperation("√"){ sqrt($0)} 簡寫為
knownOps["√"] = Op.UnaryOperation("√",sqrt)
evaluate
// 函數(shù)返回的是一個元組(Tuple),返回一個Tuple類型,里面有rusult和remainingOps
private func evaluate(ops: [Op]) -> (result: Double?,remainingOps: [Op]) {
if !ops.isEmpty {
// 這里的 let op = ops.removeLast() 會報錯,因為 ops 是一個不可變變量.
// 為什么是一個不可變變量呢?swift 里規(guī)定,傳過來的參數(shù),除了類之外其他的都是值拷貝,類為引用.
// 數(shù)組和字典都是結(jié)構(gòu)體,所以是值拷貝,這點需要牢記或衡。
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.remainingOps)
}
case .BinaryOperation(_, let operation):
let op1Evalution = evaluate(remainingOps)
if let operand1 = op1Evalution.result {
let op2Evalution = evaluate(op1Evalution.remainingOps)
if let operand2 = op2Evalution.result {
return (operation(operand1,operand2),op2Evalution.remainingOps)
}
}
}
}
return (nil,ops)
}
遞歸計算棧示例
×
4
5
6
計算過程:4×(5+6)
我要再次遞歸,用于獲取做加法的操作數(shù)
我得到5不錯,我還要再次遞歸,得到一個6焦影,填寫到這里,這樣,我計算完整個棧
就這樣,你看到我這樣通過來回遞歸,獲取操作符所需要的操作數(shù)
一旦我取得的是操作數(shù),本次遞歸就可以停止
CalculatorBrain.swift
import Foundation
// 作為計算器的Model層,這里將類命名為CalculatorBrain
class CalculatorBrain
{
private enum Op {
case Operand(Double) // 操作數(shù)
case UnaryOperation(String,Double -> Double) // 一元運算
case BinaryOperation(String,(Double,Double) ->Double) // 二元運算
var desciption: String {
get {
switch self {
case .Operand(let operand):
return "\(operand)"
case .UnaryOperation(let symbol, _):
return symbol
case .BinaryOperation(let symbol, _):
return symbol
}
}
}
}
private var opStack = [Op]()
private var knownOps = [String:Op]()
init() {
knownOps["×"] = Op.BinaryOperation("×", *)
knownOps["÷"] = Op.BinaryOperation("÷"){ $1 / $0}
knownOps["+"] = Op.BinaryOperation("+", +)
knownOps["?"] = Op.BinaryOperation("?"){ $1 - $0}
knownOps["√"] = Op.UnaryOperation("√",sqrt)
}
private func evaluate(ops: [Op]) -> (result: Double?,remainingOps: [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.remainingOps)
}
case .BinaryOperation(_, let operation):
let op1Evalution = evaluate(remainingOps)
if let operand1 = op1Evalution.result {
let op2Evalution = evaluate(op1Evalution.remainingOps)
if let operand2 = op2Evalution.result {
return (operation(operand1,operand2),op2Evalution.remainingOps)
}
}
}
}
return (nil,ops)
}
func evaluate() -> Double? {
// 令一個Tuple等于函數(shù)的返回值,_起占位作用,表示我不關(guān)心該參數(shù)。
let (result,remainder) = evaluate(opStack)
// opStack結(jié)果加上剩下的remainder
print("\(opStack) = \(result) with \(remainder) left over")
return result
}
func pushOperand(operand:Double) ->Double? {
opStack.append(Op.Operand(operand))
return evaluate()
}
func performOperation(symbol:String) -> Double? {
if let operation = knownOps[symbol] {
opStack.append(operation)
}
return evaluate()
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var display: UILabel!
var brain = CalculatorBrain()
var userIsInTheMiddleOfTypingANumber:Bool = false
@IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
print("digit = \(digit)")
}
@IBAction func operate(sender: UIButton) {
if userIsInTheMiddleOfTypingANumber
{
enter()
}
if let operation = sender.currentTitle {
if let result = brain.performOperation(operation){
displayValue = result
} else {
displayValue = 0
}
}
}
@IBAction func enter() {
userIsInTheMiddleOfTypingANumber = false
if let result = brain.pushOperand(displayValue){
displayValue = result
} else {
displayValue = 0
}
}
// 用來進棧的數(shù)據(jù)
var displayValue:Double {
get {
// 將字符串轉(zhuǎn)換為double
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set {
// 將value轉(zhuǎn)換成字符串
display.text = "\(newValue)"
// 設(shè)置重新開始輸入字符串
userIsInTheMiddleOfTypingANumber = false
}
}
}