1.字典
字典是用于存储键值对的容器
字典中的key是通过hash表存储的,基本保证key唯一对应一个value
通过键获得对应的值(可空类型,因为所给的键可能没以后相与之对应的值)
使用方法
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])")
}
// 直接通过一个元组获得字典中的键和值
for (key, value) in dict {
print("\(key) -> \(value)")
}
2. 集合
集合的存储方式使用的是哈希码(hash code)/ 散列码
集合中所有的元素都是离散存储的,相同的元素由于哈希码相同所以不会重复显示
使用方法
// 注意一定要使用Set表示集合,
// 如果不写,Swift会将其自动推断为Array
var a:Set = [1, 2, 3, 1, 2, 5]
// 添加元素
a.insert(100)
// 删除元素
a.remove(1)
var b:Set = [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.函数
定义函数
func 函数名(参数列表) -> 返回类型 { 函数执行体 }
Swift中的函数可以设定默认值
如果调用函数时没有赋值,默认使用默认值
只有当函数的返回值为Void的时候,函数执行体中没有return
调用函数
Swift中函数从第二个参数开始需要写参数名(注:在Swift3.0开始,第一个参数也需要写参数名了)
可以使用元组(tuple)让函数依次返回多条数据
使用方法
func minMax(array: [Int]) -> (min:Int, max: Int)? {
return(Min, Max)
}
Swift中的参数列表允许是可变列表(参数可以是任意多个)
使用方法
func sum(num:Int...) -> Int {
var total =0
for num in nums {
total += num
}
return total
}
print(sum(99))
在Swift中的函数还有一种输入输出参数 - inout
使用方法
func swap(inout a:Int , inout _ b:Int) -> Void {
let temp = a
a = b
b = temp
}
var a = 5 , b = 10
// 函数调用传参都是传值
swap(&a, &b)
print("a = \(a)")
print("b = \(b)")
小技巧
// 获取系统时间
let date = NSDate()
// 获取系统当前时间
let cal = NSCalendar.currentCalendar()
函数的递归调用(直接或间接的调用自身)
- 有递归公式
- 有收敛条件
递归计算1~n的和
func sum(n:Int) -> Int {
if n == 1 {
return 1
}
return n + sum(n - 1)
}
思想
求1~n的和等同于求n +函数对(n - 1)求和
将n和n之前的部分分别看成不同的2个部分。
又将(n-1)和(n-1)之前的部分分别看成2个不同的部分。
用递归求阶乘
func f(n: Int) -> Double {
if n == 0 || n == 1 {
return 1
}
return Double(n) * f( n -1 )
}
汉诺伊塔问题
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个基督教和15个非基督教徒
问题简述
杀死15个人,每次从1报数到9,杀死。然后后面一个人在报数1。在杀第9个。这样杀死15个人之后,剩下的全是基督教徒。问这30个人是如何排序的?
模型分析
首先我们需要模拟一个报数场景。目标是杀死其中15个人。所以需要创建一个变量来保存杀死的人数-counter。当counter = 15,终止循环。确定杀人的方式,每次报9的人将会被杀死。所以,需要创建一个变量来保存每个人报的数字-num。当num=9,杀死这个人。由于死人是不能报数的。所以我们可以创建一个Bool类型的值来区分活人和死人。这里我们用true表示活人,如果num=9,我们就将这个人赋值为false。因此,我们还需要创建一个存储有30个Bool值的数组。由于需要让counter=15。显然需要让30个人循环报数。因此,当循环到第30个人的时候,我们需要让他成为程序中的第0个人。这样就实现了循环报数。直到counter=15。终止循环,然后打印基督教徒和非基督教徒的站位方式。
完整代码
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..
传入年月日,返回该日是这一年的第几天
模型分析
由于1年有12个月。每个月的天数不同。所以我们创建一个数组保存每个月的天数。因为,有闰年和平年之分。所以我们还需要判断输入的年份是否为闰年。第多少天=这个月的几号+这个月之前月份的总天数。
完整代码
// 设计一个函数判断该年份是否为闰年
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..
函数作为参数传入
// 在swift中函数是一种类型
// 这也意味着函数可以作为变量或常量的类型
// 同理,函数也可以作为另一个函数的参数或者返回值
// 传入一个数组,将数组中的元素求和
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]
// 当调用foo函数时第二个参数可以传什么?
// 1. 所有自定义的(Int, Int) -> Int类型的函数
print(foo(a, fn: sum))
// 2. 传入二元运算符: + - * / % (因为运算符都是函数)
print(foo(a, fn: +))
// 3. 传入匿名函数(闭包)
// 3.1 完整闭包写法
print(foo(a, fn: { (a, b) -> Int in
return a + b
}))
// 3.2 省略类型和不必要的括号
print(foo(a, fn: { a, b in a + b }))
// 3.3 省略参数名
print(foo(a, fn: { $0 + $1 }))
// 3.4 尾随闭包
print(foo(a) { $0 + $1 })
函数的闭包
var array = ["game","abacus","hello","cat","good","internationalization",
"chaos","dislike","zealot","young"]
array.sortInPlace(>)
array.sortInPlace { $0 > $1 }
// 如果参数的最后一个参数是闭包,可以写成尾随闭包的形式
// 也就是将闭包放在函数参数的圆括号外面的花括号中
// 如果函数后面有尾随闭包且函数的圆括号中没有参数,
// 那么函数的圆括号也可以省略(仅限有尾随闭包的场景)
array.sortInPlace {
if $0.characters.count == $1.characters.count {
return $0 < $1
}
return $0.characters.count < $1.characters.count
}
print(array)
数组的3种用法
let array = [23, 37, 96, 55, 40, 92, 68, 88]
1. 过滤
过滤的关键字是filter
// 这里是留下数组中大于50的元素
let newArray1 = array.filter { $0 > 50 }
print(newArray1)
2. 映射
映射的关键字是map
// 这里是显示数组元素中所有数平方后的元素
let newArray3 = array.map { $0 * $0 }
print(newArray3)
3. 缩减
缩减的关键字是reduce
// 缩减如果是Int型的,需要一个输入初始化值
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)
小技巧
如果传入的参数不符合函数的运行要求,需要提示的时候
可以在函数内部使用断言
断言关键字为assert
Class - 类
创建一个类的步骤
这里我们用circle类来做一个例子
0. 发现类
// - 在对问题的描述里面找名词和动词
// - 名词称为类中的属性,动词会称为类中的方法
// 访问修饰符
// - public (公开)
// - internal (内部的) - 默认
// - private (私有)
1. 定义类
// - 数据抽象(属性)
// - 行为抽象(方法)
// - 初始化方法
- - -
class Circle {
// stored property
// 存储属性(保存和圆相关的数据的属性)
var center:Point
var radius: Double
// 我们可以在一个类中定义多个初始化方法
// convenience init()- 便利初始化构造器
// init()是指派初始化方法/ 指派构造器
// --用于被其他初始化方法调用
// 应当保证所有的存储属性都被初始化(有初始值)
init(center:Point, radius:Double) {
self.center = center
self.radius = radius
}
// 通常获得某个计算出的值的方法都可以设计成计算属性
// computational property
// 计算属性(通过对存储属性做运算得到的属性)
var perimeter: Double {
// 圆的周长是一个只读属性
// 所以此处只有get{}没有set{}
get {return 2 * M_PI * radius }
}
var area: Double {
get {return M_PI * radius * radius }
}
// 行为抽象
}
// 拟定一个初始圆半径
let r = 5.5
2. 创建对象
let small = Circle(center:Point(x: 0,y: 0),radius: r)
let big = Circle(center:Point(x: 0,y: 0),radius: r + 3)
3. 给对象发消息(调用对象的方法)
let fencePrice = big.perimeter * 1.5
print(NSString(format: "围墙的造价为:¥%.1f元", fencePrice))
let aislePrice = ((big.area) - small.area) * 3.5
print(NSString(format: "过道的造价为:¥%.1f元", aislePrice))
小技巧
点到点的距离调用的方法distanceTo
// 这里使用了自己已经定义好的点类
var va: Point
var vb: Point
var vc: Point
let ab = va.distanceTo(vb)
let bc = vb.distanceTo(vc)
let ca = vc.distanceTo(va)
三角形三条边计算三角形面积公式
sqrt(halfP * (halfP - ab) * (halfP - bc) * (halfP - ca))
产生一个范围类的随机数的通用公式
func randomInt(min:UInt32,_ max:UInt32) -> Int {
return Int(arc4random_uniform(max - min + 1) + min)
}
短除法
// 短除法(欧几里得算法)
// x和y的最大公约数跟y%x和x的最大公约数一样
// 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 = "♦️"
}
小技巧
/**
姓名隐去最后一个字符
*/
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 + "*"
}
}
类的多态与继承
继承: 从已有的类创建新类的过程
// 提供继承信息的称为父类(超类/基类)
// 得到继承信息的称为子类(派生类/衍生类)
// 通常子类除了得到继承信息,还会增加一些自己特有的东西
// 所以子类的能力一定比父类更强大
// 继承的意义在于子类可以复用父类的代码并且增强系统现有的功能
示例代码
(p2 as! Student).study("Swift程序与设计")
if let temp = p2 as? Teacher {
temp.teach("Java")
}
else {
print("\(p2.name)不是老师!!!")
}
多态:计算属性的重写就是多态行为
实现多态的关键步骤:
- 方法重写(子类在继承父类的过程中,对父类已有的方法进行重写)
-同时不同的子类可以给出不同的实现方式
-方法的重写的关键字是override
-重写有时也翻译为置换/覆盖/覆写
-重写的时候也可以通过super.来调用父类方法 - 对象造型(将子类当做父类型进行使用)
每天小技巧
写代码的终极原则:高内聚,低耦合
面向对象的7个原则:
1) 开闭原则;------面向扩展开放,面向修改关闭。
2) 里氏转换原则;------超类存在的地方,子类是可以替换的。
3) 依赖倒转原则;------实现尽量依赖抽象,不依赖具体实现。
4) 接口隔离原则;------应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。
5) 组合/聚合复用原则;------尽量使用合成/聚合达到复用,尽量少用继承。
原则:一个类中有另一个类的对象。
6) “迪米特”法则;------又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。
7) 单一职责原则。-----每一个类应该专注于做一件事情。