1.字典
字典是用于存儲(chǔ)鍵值對(duì)的容器
字典中的key是通過(guò)hash表存儲(chǔ)的,基本保證key唯一對(duì)應(yīng)一個(gè)value
通過(guò)鍵獲得對(duì)應(yīng)的值(可空類型,因?yàn)樗o的鍵可能沒以后相與之對(duì)應(yīng)的值)
使用方法
var dict: [String:String] = ["abacus":"算盤", "abnormal":"異常的","hello":"你好","good":"好的"]
key -> value
print(dict["hello"])
// 添加元素
dict["delicious"] = "美味的"
// 刪除元素
dict.removeValueForKey("hello")
dict["hello"] = nil
// 修改元素
dict["delicious"] = "難吃的"
// 遍歷字典中的所有值
for value in dict.values {
print(value)
}
// 遍歷字典中的所有鍵
for key in dict.keys {
print("\(key) -> \(dict[key])")
}
// 直接通過(guò)一個(gè)元組獲得字典中的鍵和值
for (key, value) in dict {
print("\(key) -> \(value)")
}
2. 集合
集合的存儲(chǔ)方式使用的是哈希碼(hash code)/ 散列碼
集合中所有的元素都是離散存儲(chǔ)的,相同的元素由于哈希碼相同所以不會(huì)重復(fù)顯示
使用方法
// 注意一定要使用Set表示集合,
// 如果不寫,Swift會(huì)將其自動(dòng)推斷為Array
var a:Set<Int> = [1, 2, 3, 1, 2, 5]
// 添加元素
a.insert(100)
// 刪除元素
a.remove(1)
var b:Set<Int> = [3, 5, 7, 9, 11]
// 交集
print(a.intersect(b))
// 并集
print(a.union(b))
// 差集(a有但是b沒有的元素)
print(a.subtract(b))
// 判斷a和b是否是同一集合 返回true相同 false不相同
print(a == b)
// 判斷b是否是a的子集
print(b.isSubsetOf(a))
// 判斷b是否是a的超集
print(b.isSupersetOf(a))
// 判斷a和b是否有相同元素 返回true沒有 false有
print(a.isDisjoinWith(b))
3.函數(shù)
定義函數(shù)
func 函數(shù)名(參數(shù)列表) -> 返回類型 { 函數(shù)執(zhí)行體 }
Swift中的函數(shù)可以設(shè)定默認(rèn)值
如果調(diào)用函數(shù)時(shí)沒有賦值,默認(rèn)使用默認(rèn)值
只有當(dāng)函數(shù)的返回值為Void的時(shí)候,函數(shù)執(zhí)行體中沒有return
調(diào)用函數(shù)
Swift中函數(shù)從第二個(gè)參數(shù)開始需要寫參數(shù)名(注:在Swift3.0開始,第一個(gè)參數(shù)也需要寫參數(shù)名了)
可以使用元組(tuple)讓函數(shù)依次返回多條數(shù)據(jù)
使用方法
func minMax(array: [Int]) -> (min:Int, max: Int)? {
return(Min, Max)
}
Swift中的參數(shù)列表允許是可變列表(參數(shù)可以是任意多個(gè))
使用方法
func sum(num:Int...) -> Int {
var total =0
for num in nums {
total += num
}
return total
}
print(sum(99))
在Swift中的函數(shù)還有一種輸入輸出參數(shù) - inout
使用方法
func swap(inout a:Int , inout _ b:Int) -> Void {
let temp = a
a = b
b = temp
}
var a = 5 , b = 10
// 函數(shù)調(diào)用傳參都是傳值
swap(&a, &b)
print("a = \(a)")
print("b = \(b)")
小技巧
// 獲取系統(tǒng)時(shí)間
let date = NSDate()
// 獲取系統(tǒng)當(dāng)前時(shí)間
let cal = NSCalendar.currentCalendar()
函數(shù)的遞歸調(diào)用(直接或間接的調(diào)用自身)
- 有遞歸公式
- 有收斂條件
遞歸計(jì)算1~n的和
func sum(n:Int) -> Int {
if n == 1 {
return 1
}
return n + sum(n - 1)
}
思想
求1~n的和等同于求n +函數(shù)對(duì)(n - 1)求和
將n和n之前的部分分別看成不同的2個(gè)部分。
又將(n-1)和(n-1)之前的部分分別看成2個(gè)不同的部分。
用遞歸求階乘
func f(n: Int) -> Double {
if n == 0 || n == 1 {
return 1
}
return Double(n) * f( n -1 )
}
漢諾伊塔問(wèn)題
func haoi(n:Int,_ a:String, _ b:String, _ c:String) {
if n > 0 {
haoi(n - 1, a, c, b)
print("\(a) --> \(b)")
haoi(n - 1, c, b, a)
}
}
haoi(1,"A" ,"B" ,"C")
15個(gè)基督教和15個(gè)非基督教徒
問(wèn)題簡(jiǎn)述
殺死15個(gè)人,每次從1報(bào)數(shù)到9,殺死。然后后面一個(gè)人在報(bào)數(shù)1。在殺第9個(gè)。這樣殺死15個(gè)人之后,剩下的全是基督教徒。問(wèn)這30個(gè)人是如何排序的?
模型分析
首先我們需要模擬一個(gè)報(bào)數(shù)場(chǎng)景。目標(biāo)是殺死其中15個(gè)人。所以需要?jiǎng)?chuàng)建一個(gè)變量來(lái)保存殺死的人數(shù)-counter。當(dāng)counter = 15,終止循環(huán)。確定殺人的方式,每次報(bào)9的人將會(huì)被殺死。所以,需要?jiǎng)?chuàng)建一個(gè)變量來(lái)保存每個(gè)人報(bào)的數(shù)字-num。當(dāng)num=9,殺死這個(gè)人。由于死人是不能報(bào)數(shù)的。所以我們可以創(chuàng)建一個(gè)Bool類型的值來(lái)區(qū)分活人和死人。這里我們用true表示活人,如果num=9,我們就將這個(gè)人賦值為false。因此,我們還需要?jiǎng)?chuàng)建一個(gè)存儲(chǔ)有30個(gè)Bool值的數(shù)組。由于需要讓counter=15。顯然需要讓30個(gè)人循環(huán)報(bào)數(shù)。因此,當(dāng)循環(huán)到第30個(gè)人的時(shí)候,我們需要讓他成為程序中的第0個(gè)人。這樣就實(shí)現(xiàn)了循環(huán)報(bào)數(shù)。直到counter=15。終止循環(huán),然后打印基督教徒和非基督教徒的站位方式。
完整代碼
var array = [Bool](count: 30, repeatedValue: true)
var count = 0
var num = 0
var i = 0
while count < 15 {
if array[i] {
num += 1
if num == 9 {
array[i] = false
count += 1
num = 0
}
}
i += 1
if i == 30 {
i = 0
}
}
for i in 0..<array.count{
print(array[i] ? "基" : "非",terminator:"")
}
傳入年月日,返回該日是這一年的第幾天
模型分析
由于1年有12個(gè)月。每個(gè)月的天數(shù)不同。所以我們創(chuàng)建一個(gè)數(shù)組保存每個(gè)月的天數(shù)。因?yàn)?,有閏年和平年之分。所以我們還需要判斷輸入的年份是否為閏年。第多少天=這個(gè)月的幾號(hào)+這個(gè)月之前月份的總天數(shù)。
完整代碼
// 設(shè)計(jì)一個(gè)函數(shù)判斷該年份是否為閏年
func isLeapYear(year:Int) -> Bool {
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0
}
func daysOfYear(year: Int, month: Int, day: Int) -> Int {
var daysOfMonth = [31,28,31,30,31,30,31,31,30,31,30,31]
if isLeapYear(year) {
daysOfMonth[1] = 29
}
var sum = 0
for days in daysOfMonth[0..<count - 1] {
sum += days
}
for i in 0..<month - 1 {
sum += daysOfMonth[i]
}
return sum + day
}
函數(shù)作為參數(shù)傳入
// 在swift中函數(shù)是一種類型
// 這也意味著函數(shù)可以作為變量或常量的類型
// 同理,函數(shù)也可以作為另一個(gè)函數(shù)的參數(shù)或者返回值
// 傳入一個(gè)數(shù)組,將數(shù)組中的元素求和
func foo(array:[Int],fn:(Int,Int) -> Int) -> Int {
var sum = 0
for x in array {
sum = fn(sum, x)
}
return sum
}
let a = [1, 2, 3, 4, 5]
// 當(dāng)調(diào)用foo函數(shù)時(shí)第二個(gè)參數(shù)可以傳什么?
// 1. 所有自定義的(Int, Int) -> Int類型的函數(shù)
print(foo(a, fn: sum))
// 2. 傳入二元運(yùn)算符: + - * / % (因?yàn)檫\(yùn)算符都是函數(shù))
print(foo(a, fn: +))
// 3. 傳入匿名函數(shù)(閉包)
// 3.1 完整閉包寫法
print(foo(a, fn: { (a, b) -> Int in
return a + b
}))
// 3.2 省略類型和不必要的括號(hào)
print(foo(a, fn: { a, b in a + b }))
// 3.3 省略參數(shù)名
print(foo(a, fn: { $0 + $1 }))
// 3.4 尾隨閉包
print(foo(a) { $0 + $1 })
函數(shù)的閉包
var array = ["game","abacus","hello","cat","good","internationalization",
"chaos","dislike","zealot","young"]
array.sortInPlace(>)
array.sortInPlace { $0 > $1 }
// 如果參數(shù)的最后一個(gè)參數(shù)是閉包,可以寫成尾隨閉包的形式
// 也就是將閉包放在函數(shù)參數(shù)的圓括號(hào)外面的花括號(hào)中
// 如果函數(shù)后面有尾隨閉包且函數(shù)的圓括號(hào)中沒有參數(shù),
// 那么函數(shù)的圓括號(hào)也可以省略(僅限有尾隨閉包的場(chǎng)景)
array.sortInPlace {
if $0.characters.count == $1.characters.count {
return $0 < $1
}
return $0.characters.count < $1.characters.count
}
print(array)
數(shù)組的3種用法
let array = [23, 37, 96, 55, 40, 92, 68, 88]
1. 過(guò)濾
過(guò)濾的關(guān)鍵字是filter
// 這里是留下數(shù)組中大于50的元素
let newArray1 = array.filter { $0 > 50 }
print(newArray1)
2. 映射
映射的關(guān)鍵字是map
// 這里是顯示數(shù)組元素中所有數(shù)平方后的元素
let newArray3 = array.map { $0 * $0 }
print(newArray3)
3. 縮減
縮減的關(guān)鍵字是reduce
// 縮減如果是Int型的,需要一個(gè)輸入初始化值
let result1 = array.reduce(0, combine: +)
print(result1)
let reslut2 = array.reduce(1, combine: *)
print(reslut2)
let max = array[0]
let reslut3 = array.reduce(array[0]) {
$1 > $0 ? $1 : $0
}
print(reslut3)
let strArray = ["I","love","you"]
// 如果是字符串類型的,可以省略為空字符,但不能是空
let reslut4 = strArray.reduce("") { $0 + " " + $1 }
print(reslut4)
小技巧
如果傳入的參數(shù)不符合函數(shù)的運(yùn)行要求,需要提示的時(shí)候
可以在函數(shù)內(nèi)部使用斷言
斷言關(guān)鍵字為assert
Class - 類
創(chuàng)建一個(gè)類的步驟
這里我們用circle類來(lái)做一個(gè)例子
0. 發(fā)現(xiàn)類
// - 在對(duì)問(wèn)題的描述里面找名詞和動(dòng)詞
// - 名詞稱為類中的屬性,動(dòng)詞會(huì)稱為類中的方法
// 訪問(wèn)修飾符
// - public (公開)
// - internal (內(nèi)部的) - 默認(rèn)
// - private (私有)
1. 定義類
// - 數(shù)據(jù)抽象(屬性)
// - 行為抽象(方法)
// - 初始化方法
- - -
class Circle {
// stored property
// 存儲(chǔ)屬性(保存和圓相關(guān)的數(shù)據(jù)的屬性)
var center:Point
var radius: Double
// 我們可以在一個(gè)類中定義多個(gè)初始化方法
// convenience init()- 便利初始化構(gòu)造器
// init()是指派初始化方法/ 指派構(gòu)造器
// --用于被其他初始化方法調(diào)用
// 應(yīng)當(dāng)保證所有的存儲(chǔ)屬性都被初始化(有初始值)
init(center:Point, radius:Double) {
self.center = center
self.radius = radius
}
// 通常獲得某個(gè)計(jì)算出的值的方法都可以設(shè)計(jì)成計(jì)算屬性
// computational property
// 計(jì)算屬性(通過(guò)對(duì)存儲(chǔ)屬性做運(yùn)算得到的屬性)
var perimeter: Double {
// 圓的周長(zhǎng)是一個(gè)只讀屬性
// 所以此處只有g(shù)et{}沒有set{}
get {return 2 * M_PI * radius }
}
var area: Double {
get {return M_PI * radius * radius }
}
// 行為抽象
}
// 擬定一個(gè)初始圓半徑
let r = 5.5
2. 創(chuàng)建對(duì)象
let small = Circle(center:Point(x: 0,y: 0),radius: r)
let big = Circle(center:Point(x: 0,y: 0),radius: r + 3)
3. 給對(duì)象發(fā)消息(調(diào)用對(duì)象的方法)
let fencePrice = big.perimeter * 1.5
print(NSString(format: "圍墻的造價(jià)為:¥%.1f元", fencePrice))
let aislePrice = ((big.area) - small.area) * 3.5
print(NSString(format: "過(guò)道的造價(jià)為:¥%.1f元", aislePrice))
小技巧
點(diǎn)到點(diǎn)的距離調(diào)用的方法distanceTo
// 這里使用了自己已經(jīng)定義好的點(diǎn)類
var va: Point
var vb: Point
var vc: Point
let ab = va.distanceTo(vb)
let bc = vb.distanceTo(vc)
let ca = vc.distanceTo(va)
三角形三條邊計(jì)算三角形面積公式
sqrt(halfP * (halfP - ab) * (halfP - bc) * (halfP - ca))
產(chǎn)生一個(gè)范圍類的隨機(jī)數(shù)的通用公式
func randomInt(min:UInt32,_ max:UInt32) -> Int {
return Int(arc4random_uniform(max - min + 1) + min)
}
短除法
// 短除法(歐幾里得算法)
// x和y的最大公約數(shù)跟y%x和x的最大公約數(shù)一樣
// Greatest Common Divisor
func gcd(x:Int ,_ y: Int) -> Int {
if x > y {
return gcd(y, x)
}
else if y % x != 0 {
return gcd(y % x, x)
}
else {
return x
}
}
枚舉
例子 - 花色的枚舉
enum Suite: String {
case Spade = "??"
case Heart = "??"
case Club = "??"
case Diamond = "??"
}
小技巧
/**
姓名隱去最后一個(gè)字符
*/
var name: String {
get {
let value = _name.characters.count > 2 ? -2 : -1
// let displayName = _name.substringToIndex(_name.endIndex.predecessor())
let displayName = _name.substringToIndex(_name.endIndex.advancedBy(value))
return displayName + "*"
}
}
類的多態(tài)與繼承
繼承: 從已有的類創(chuàng)建新類的過(guò)程
// 提供繼承信息的稱為父類(超類/基類)
// 得到繼承信息的稱為子類(派生類/衍生類)
// 通常子類除了得到繼承信息,還會(huì)增加一些自己特有的東西
// 所以子類的能力一定比父類更強(qiáng)大
// 繼承的意義在于子類可以復(fù)用父類的代碼并且增強(qiáng)系統(tǒng)現(xiàn)有的功能
示例代碼
(p2 as! Student).study("Swift程序與設(shè)計(jì)")
if let temp = p2 as? Teacher {
temp.teach("Java")
}
else {
print("\(p2.name)不是老師!?。?)
}
多態(tài):計(jì)算屬性的重寫就是多態(tài)行為
實(shí)現(xiàn)多態(tài)的關(guān)鍵步驟:
- 方法重寫(子類在繼承父類的過(guò)程中,對(duì)父類已有的方法進(jìn)行重寫)
-同時(shí)不同的子類可以給出不同的實(shí)現(xiàn)方式
-方法的重寫的關(guān)鍵字是override
-重寫有時(shí)也翻譯為置換/覆蓋/覆寫
-重寫的時(shí)候也可以通過(guò)super.來(lái)調(diào)用父類方法 - 對(duì)象造型(將子類當(dāng)做父類型進(jìn)行使用)
每天小技巧
寫代碼的終極原則:高內(nèi)聚,低耦合
面向?qū)ο蟮?個(gè)原則:
1) 開閉原則;------面向擴(kuò)展開放,面向修改關(guān)閉。
2) 里氏轉(zhuǎn)換原則;------超類存在的地方,子類是可以替換的。
3) 依賴倒轉(zhuǎn)原則;------實(shí)現(xiàn)盡量依賴抽象,不依賴具體實(shí)現(xiàn)。
4) 接口隔離原則;------應(yīng)當(dāng)為客戶端提供盡可能小的單獨(dú)的接口,而不是提供大的總的接口。
5) 組合/聚合復(fù)用原則;------盡量使用合成/聚合達(dá)到復(fù)用,盡量少用繼承。
原則:一個(gè)類中有另一個(gè)類的對(duì)象。
6) “迪米特”法則;------又叫最少知識(shí)原則,一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少的與其他實(shí)體發(fā)生相互作用。
7) 單一職責(zé)原則。-----每一個(gè)類應(yīng)該專注于做一件事情。