Swift 語(yǔ)言初探2

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)用自身)

  1. 有遞歸公式
  2. 有收斂條件

遞歸計(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)鍵步驟:

  1. 方法重寫(子類在繼承父類的過(guò)程中,對(duì)父類已有的方法進(jìn)行重寫)
    -同時(shí)不同的子類可以給出不同的實(shí)現(xiàn)方式
    -方法的重寫的關(guān)鍵字是override
    -重寫有時(shí)也翻譯為置換/覆蓋/覆寫
    -重寫的時(shí)候也可以通過(guò)super.來(lái)調(diào)用父類方法
  2. 對(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)該專注于做一件事情。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容