Swift 基础##
目录
控制流的代码优化
字符串代码注意
swift 的数组
字典和集合
函数
<a name="控制流代码优化"></a>控制流的代码优化
如果 if ... else 的结构太过繁琐######
那么就尽量尝试使用 guard(保证)的代码风格,注意:不要忘记return
func buy( money: Int , price: Int , space: Int , volum: Int){
guard money >= price else {
print("Your money is not enough")
return
}
guard space >= volum else {
print("Your space is not enough")
return
}
print("You can buy it")
print("You also have \(money - price) $")
print("You also have \(space - volum) space")
}
buy(money: 100, price: 80, space: 50, volum: 20)
用 _
忽略要表达的值
在 swift 中可以是用下划线来忽略一些不关心的值。
//使用元组---使用下划线来代替不关心的值
let loginResult = (true , "name")
let (isLoginSuccess , _) = loginResult
if isLoginSuccess {
print("Loing Success")
}else{
print("Login Failed")
}
元组(Tuple)的使用方法
元组是将多个不同类型的数据,放在同一个数据类型中。
元组可以有任意多的值,不同的值可以是不同类型。
//声明元组---基本
var point = (5 , 2)
var httpResponse = ( 404 , "Not Found")
point.0
point.1
//声明元组---显式的指定元组的类型
var point2:(Int , Int , Int) = (1 , 2 , 3)
var httpResponse2:(Int , String) = (200 , "Ok")
//声明元组---为元组的各个分量赋值
let point3 = (x: 1 , y: 2)
point3.x
point3.y
//声明元组---显式指定类型并赋值
let point4:( x: Int , y: Int) = ( 1 , 2)
point4.x
point4.y
//使用元组---解包的方式使用元组中的分量
let ( x , y) = point
let (statusCode , statusMessage) = httpResponse
//使用元组---使用下划线来代替不关心的值
let loginResult = (true , "name")
let (isLoginSuccess , _) = loginResult
if isLoginSuccess {
print("Loing Success")
}else{
print("Login Failed")
}
控制转移的另一种方法
以前在面对复杂的循环结构,在转移的时候都是一层一层的break
var gotAnswer = false
for m in 1...300 {
for n in 1..300{
if m*m*m*m - n*n == 15*m*n{
print( m , n)
gotAnswer = true
break
}
}
if gotAnswer {
break
}
}
那么现在可以用另一种方式来控制转移
//另一种方式控制转移
finsAnswer:
for m in 1...300 {
for n in 1...300{
if m*m*m*m - n*n == 15*m*n{
print( m , n)
//break 掉 findAnswer 这层循环
break findAnswer
}
}
}
case where 配合使用,可以大幅度的精简代码
//case where 配合使用可以缩短代码
//在 switch 中使用 where
let point5 = (3,3)
switch point5 {
case let (x,y) where x == y:
print("It is on the line x == y")
case let (x,y) where x == -y:
print("It is on the line x == -y")
case let (x, y):
print("It is just an ordinary point")
print("The point is \(x), \(y)")
}
//在 if 中进行模式限定的效果
var age = 19
if case 10...19 = age {
print("You are a teenager")
}
//在 if 中使用where 的方法 , 在 if case where 中可以省略 where
if case 10...19 = age , age >= 18 {
print("You are a teenager and in a collage.")
}
//另一个例子
let vector = (4,0)
//在限定的时候才用解包 , 同时 where 还是要省略
if case (let x, 0) = vector , x > 2 && x < 5 {
print("It is the vector")
}
//在循环中使用 where 的方法
for i in 1...100{
if 1%3 == 0 {
print(i)
}
}
for case let i in 1...100 where i%3 == 0 {
print(i)
}
<a name="字符串代码注意"></a>字符串代码注意
在 swift 语言中存在很多对于字符串的处理方法,并且在 swift3中存在很多的方法命名上的更新。对于 OC语言保留下来的 NSString 对于字符串的处理在某些方面比 swift 的 String 要存在优势,所以,存在要求处理繁琐的字符串的时候可以考虑使用 OC 的 NSString。
import UIKit
let str = "Hello, swift"
// 本小节所涉及的API,大多数也经过了Swift3的化简。请大家体会Swift3对调用的字符串方法名称的化简。
// 大小写转换
//swift2: str.uppercaseString
str.uppercased()
//swift2: str.lowercaseString
str.lowercased()
//swift2: str.capitalizedString
str.capitalized
// 使用String的方法
//swift2: str.containsString("Hello")
str.contains("Hello")
str.hasPrefix("Hello")
str.hasSuffix("swift")
// String的缺点
let s = "one third is \(1.0/3.0)"
print(s)
// 注意:现在Swift中的String和OC中的NSString之间的界限越来越小,如使用format初始化一个String,在Swift2中已经可以了。具体代码如下:
let ss = String(format: "one third is %.2f", 1.0/3.0)
// as String
let s2 = NSString(format: "one third is %.2f😀", 1.0/3.0) as String
print(s2)
// NSString
var s3:NSString = "one third is 0.33😀"
//swift2: s3.substringFromIndex(4)
s3.substring(from: 4)
//swift2: s3.substringToIndex(3)
s3.substring(to: 3)
//swift2: s3.substringWithRange(NSMakeRange(4, 5))
s3.substring(with: NSMakeRange(4, 5))
// String和NSString的不同
let s4 = "😀😀😀"
let s5:NSString = "😀😀😀"
s4.characters.count
s5.length
let s6 = " --- Hello ----- " as NSString
//swift2: s6.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: " -"))
s6.trimmingCharacters(in: CharacterSet(charactersIn: " -"))
//swift2: let range = s6.rangeOfString("ll")
let range = s6.range(of: "ll")
range.location
range.length
//swift2: s6.stringByReplacingOccurrencesOfString("He", withString: "Apo")
s6.replacingOccurrences(of: "He", with: "Apo")
可选型(Optional)
可选型可以让我们在某个具体类型和 nil
之间进行一个选择,可选型既可以存放一个具体类型,也可以存放nil
(空)。如果想让一个变量成为可选型,必须显式声明这个变量为可选型变量。
//必须显式声明这个这是个可选型的变量,否则,编译器不知道把类型解析为 Int? 还是 String?还是别的
var errorCode:Int? = 404
//这个的输出就是 Optional(404)
print(errorCode)
可选性的解包
对可选型的解包使用,一来可以强制解包,但是存在很多不安全的因素。第二是才用 swift 的简洁的解包方式,可以同时解多个包。另外需要注意到,在 swift 3.0中对于解多个包的情况,每个变量都需要声明 let 或者 var ,在 if 中使用 where,在 swift3.0中也已经用英文逗号取代 where 关键字了。
var errorCode:String? = "404"
print(errorCode)
//Unwrap 解包 !就是强制解包,但是存在风险,如果这个值不存在,那么就可能报错了
"The errorCode is " + errorCode!
//最繁琐的解包方式
if errorCode != nil {
"The errorCode is " + errorCode!
}else{
"No error"
}
//swift 的简化的解包方式
if let errorCode = errorCode {
"The errorCode is " + errorCode
}
//声明一个可选性的错误消息提示
var errorMessage:String? = "Not found"
//繁琐的解多个包方的方式
if let errorCode = errorCode {
if let errorMessage = errorMessage {
print("errorCode is \(errorCode), errorMessage is \(errorMessage)")
}
}
//swift简化的解包方式
if let errorCode = errorCode , let errorMessage = errorMessage {
print("errorCode is \(errorCode), errorMessage is \(errorMessage)")
}
//当然这里用到的是 if 语句,当然也可以使用if where 来过滤,注意在 swit3.0中 where 被替代成了英文的逗号
if let errorCode = errorCode , let errorMessage = errorMessage , errorCode == "404" {
print("Page not found")
}
Optional Chaining(可选型链条)
swift 在判定条件复杂的时候,存在很多个 if let errorMessage = errorMessage { errorMessage.uppercased()}
进行解包的时候,就会变得非常的繁琐,所以 swift 有了 Optional Chaining。
var errorMessage:String? = "Not Found"
//传统的解包方法,解包完成后对包内容的大写转换
if let errorMessage = errorMessage{
errorMessage.uppercased()
}
//Optional Chaining 的方法,如果 errorMessage 有值,那么就执行转换大写的操作。
errorMessage?.uppercased()
//除此之外呢,还可以搭配 if 使用
if let errorMessage = errorMessage?.uppercased() {
errorMessage
}
nil coalesce 的风格
当想给一个变量赋值,但是要依情况而定的时候,可能会考虑到使用 if ... else
或者三元运算符let message = message1! == nil ? "No error" : message1
,那么除此之外还可以使用nil coalsece 的风格来简化代码
var errorMessage:String? = nil
//想给你一个值赋上初值,但是要依情况而定
let message: String
//最老的方式,就是先来判断errorMessage 有没有值,有的话就赋值为 errorMessage 的值,没有就赋值为“No error”
if let errorMessage = errorMessage {
message = errorMessage
}else{
message = "No error"
}
//用三目运算符的方式(这里要对 errorMessage 进行强制解包)
let message2 = errorMessage! == nil ? "No error" : errorMessage
//采用 nil coalesce 的方法
let message3 = errorMessage?? "No error"
可选型在元组中的应用
对于元组可以声明为可选型,元组中的元素也可以声明为可选型。
//对于元组中的元素声明为可选型,这时候只能是元组中的 errorMessage这个元素是可选型的
var error1 : (errorCode: Int , errorMessage: String?) = (404 ,"Not Found")
//对于元组声明为可选型,这时候只有元组是可选型的,里面的元素不是可选型。
var error2 : (errorCode: Int , errorMessage: String)? = (404 , "Not Found")
//对于元组和元组里的元素都是可选型的。
var error3 : (errorCode: Int , errorMessage: String?)? = (404,"Not Found")
可选型在实际情况下的应用
//可选型的实际应用,在转换不一定能成功的地方,才用可选型。
var ageInt: String = "xyz"
var age = Int(ageInt)
//采用 if let 的方式解包,并用 where 的方式来限定条件
var ageInt2: String = "17"
if let age = Int(ageInt2) , age < 20 {
print("You are a teenager!")
}
隐式可选型
隐式可选值其实就是把显式声明的可选值用的问号String?
换成感叹号String!
,隐式可选值可以在使用的时候不进行解包使用,一旦隐式可选值的值为 nil,那么程序就可能会崩溃,所以隐式可选值是不安全的。
隐式可选型存在的意义是,用户可以暂时存放一个 nil 值,但是当程序执行之后,还可以存放相应的值。
//但是隐式可选型的意义在于,在定义一些类的时候,当类刚创建的时候,有些参数是可以是没有值的,但是随着函数的执行才会有值
var errorMessage:String!
errorMessage = "Not found"
"The messge is " + errorMessage
<a name="array"></a> swift 的数组
数组是有序的数据集合
对于数组的声明和初始化方法
//声明数组
var emptyArray1: [Int] = []
var emptyArray2: Array<Int> = []
var emptyArray3 = [Int]()
var emptyArray4 = Array<Int>()
//数组的初始化
//初始化一个含有5个元素的并且全是0的数组
var allZearos = [Int](repeating: 0 , count:5)
var allZearo = Array<Int>(repeating:0 , count:5)
对于数组的基本操作
var numbers:Array<Int> = [1,3,4,5]
//数组计数
numbers.count
numbers.isEmpty
//索引值,注意索引越界
numbers[2]
//快速的访问数组的第一个值,但是因为不确定数组的第一个值是否存在,所以会是可选型。
numbers.first
numbers.last
//在可选型数组元素中可以要进行解包
if let firstNumber = numbers.first {
print("This is the first \(firstNumber)")
}
//如果确定数组会存在第一个或者最后一个值存在,那么也可以是使用强制解包。
numbers.first!
//这样获取数组最后一个值的好处就是不需要进行解包
numbers[numbers.count-1]
//获取数组中最大/最小值
numbers.min()
numbers.max()
//取出数组索引中一段的值
numbers[1..<3]
numbers[1...numbers.count-1]
//是否包含这个元素
numbers.contains(1)
//查看索引的值在数组中的位置
numbers.index(of: 1)
//遍历数组的元素
for number in numbers{
print(number)
}
//遍历数组元素并获得索引
for (i , val) in numbers.enumerated(){
print("\(i+1): \(val)")
}
var oneToFive = [1,3,4,5]
//数组之间是可以比较的
numbers == oneToFive
对于数组的增删改
对于涉及数组索引的部分,一定要谨慎数组越界的问题
//对数组的增、删、改
var course = ["玩转 swift 第一季" , "玩转 swift 第二季" , "玩转 swift 第三季"]
//追加数组
course.append("玩转 swift 第四季")
course += ["swift 面向对象编程"]
course = course + ["Swift 设计模式"]
//插入数组,注意要小心数组越界
course.insert("swift 面向协议编程", at: 4)
//删除数组中的最后一个元素并返回
course.removeLast()
//删除数组中的第一个元素并返回
course.removeFirst()
//删除某一个数组元素,删除当前的数组元素并返回,还是要注意数组越界的错误
course.remove(at: 3)
//删除一定范围内的数组,同样注意不要产生数组越界
course.removeSubrange(2..<course.count)
//删除所有
course.removeAll()
print(course)
//复制粘贴代码的时候,请先注释掉对于删除数组元素的部分。
//改变数组元素的值,注意数组越界
course[0] = "本季玩转 swift 课程已经改变"
//修改一个范围内的数组元素,修改的数组元素不需要和修改区间内元素的个数一致
course[1...3] = ["这里是范围修改的数组元素","这里也是修改范围的数组"]
print(course)
<a name="字典和集合"></a>字典和集合
字典是存储 键 - 值
的无序数据集
字典的声明和查询
//声明字典的三种方式
var dic = ["swift": "雨燕", "python": "大蟒", "java": "爪哇岛"]
var dic1:[String : String] = ["swift": "雨燕", "python": "大蟒", "java": "爪哇岛"]
var dic2: Dictionary<String , String> = ["swift": "雨燕", "python": "大蟒", "java": "爪哇岛"]
//声明一个空的字典
var dic3:[String : Int] = [:]
var dic4:Dictionary<String , Int> = [:]
var dic5 = [String : Int]()
var dic6 = Dictionary<String , Int>()
//查询字典的值
//使用键索引一个字典返回的是一个可选型,因为我们很容易就使用一个字典中不存在的键来访问
dic["swift"]
//既然返回的是一个可选型的值,那么使用的时候就需要进行解包
if let value = dic["swift"] {
print("swift's value is \(value)")
}
//查询字典的数量
dic.count
//判断字典是否为空
dic.isEmpty
//返回字典的所有键
Array(dic.keys)
//返回字典中所有值
Array(dic.values)
//打印所有字典的键,注意字典是一个无序的集合,所以打印的时候的顺序不一定跟初始化时候的一致
for key in dic.keys{
print(key)
}
//同理,可以打印打印所有的值
for value in dic.values{
print(value)
}
//很多时候我们同时需要键和值,那么把两个参数放在元组中,以前我们使用数组的时候,要把调用 dic.enumerated(),现在字典本身就是键值对,所以直接使用 dic,更加容易了。
for (key , value) in dic{
print("\(key) -> \(value)")
}
//两个字典之间可以进行值的比较,只要字典里的键值对对应,不区分顺序
dic == dic1
字典的增删改
//声明一个字典
var info = ["name":"Erlich Liu","password":"Swift","occupation":"student"]
//对字典的值进行修改
info["name"] = "Liu"
//使用updateValue()方法,可以返回上一个键值
info.updateValue("Erlich Liu", forKey: "password")
let oldPassword = info.updateValue("Erlich Liu", forKey: "password")
//let newPassword = info["password"]
if let oldPassword = oldPassword , let newPssword = info["password"] , newPssword == oldPassword {
print("新密码与原始密码一样,可能会导致安全问题")
}
//当你检索的键不存在的时候,就是添加值,在字典中不存在数组越界的问题。
info["course"] = "Swift"
info.updateValue("web", forKey: "web")
//当检索的不存在的键的时候,返回的值会是 nil,所以删除一个存在的键,也可以是直接检索到这个键值,然后给他赋值成 nil
info["course"] = nil
//删除一个键值,并返回当前的键值
info.removeValue(forKey: "name")
//删除所有
info.removeAll()
集合
集合可以将一些无序的元素放在一起,其实看上去跟数组几乎一样,但是要显式的声明这个是集合。集合可以执行一些特殊的操作,比如自动除去重复的元素、可以将两个集合中元素组合起来。
//显式声明一个集合,集合可以自动去除重复的元素
var skillsOf:Set<String> = ["swift","OC","OC"]
var set1: Set<Int> = []
var set2 = Set<String>()
//再次强调集合是不包括重复的
var set3: Set<Int> = [1,1,1]
set3.count
//在集合里可以理解为在集合里随机挑选出一个值,但是因为在空集合里的话,是取不出来值的,所以会是一个可选值
let s = skillsOf.first
//集合中的元素也是可以进行比较的,再次强调了集合是会去除重复元素的。
var set4: Set<Int> = [1,2]
var set5: Set<Int> = [1,1,1,1,2,2,2]
set4 == set5
集合特有的一些操作
//分别设计三个学生的技能
var skillOfA:Set<String> = ["swift","OC","java"]
var skillOfB:Set<String> = ["HTML","CSS","JavaScript"]
var skillOfC:Set<String> = ["swift"]
//向集合中添加元素
skillOfA.insert("CSS")
skillOfB.insert("PHP")
skillOfC.insert("C")
//注意在这些方法中,参数只要是 sequence 的,都可以传入一个数组或者集合的值
//集合�操作:合并
//将两个集合中的元素组合起来,使用 union() 组合是不改变每个集合的值的
skillOfB.union(skillOfA)
//使用 formUnion() 可以将skillOfB 集合的元素添加到 skillOfA的集合中
skillOfA.formUnion(skillOfB)
//交集操作:共有
//用 intersection() 可以看到并返回两个集合都有的元素,但是并不会改变任何一个集合中的元素
skillOfA.intersection(skillOfB)
//用 formIntersection() 方法可以返回两者都有的元素,并将共有的元素重新赋值给第一个集合
skillOfA.formIntersection(skillOfB)
//集合操作:减法
//使用 subtract()方法是找出 skillOfA 独有的而 skillOfB 不具备的,并不会改变任何一个集合的元素
skillOfA.subtract(skillOfC)
//使用 formSubtract()方法是找出 skillOfA 独有的,并且将 skillOfA 独有的值重新赋值给 skillOfA
skillOfA.subtracting(skillOfC)
//亦或操作:两个集合中只出现一次的那些元素
//使用symmetricDifference()方法找出来两个集合中只出现一次的那些元素,并不会改变任何一个集合的元素
skillOfA.symmetricDifference(skillOfB)
//使用 formsymmetricDifferebce()方法找出两个集合中只出现过一次的元素,并且把这些元素重新赋值给第一个集合
skillOfA.formSymmetricDifference(skillOfB)
//判断是否是子集
skillOfA.isSubset(of: skillOfB)
//判断是不是真子集
skillOfA.isStrictSubset(of: skillOfB )
//判断是不是超集
skillOfA.isSuperset(of: skillOfB)
//判断是不是真超集
skillOfA.isStrictSuperset(of: skillOfB)
//判断是不是相离集合,就是这两个集合有没有共有的元素
skillOfA.isDisjoint(with: skillOfB)
选择合适的数据结构
==数组: 有序==
==集合:无序、唯一性、提供集合操作、快速查找==
==字典:键-值数据对==
<a name="fun"></a>函数
内部参数和外部参数
内部参数和外部参数同时保证了函数内部的语义明确,又保证了函数外部分语义明确。减少了开发者需要查询文档的次数。其实外部函数就是在内部函数之前给一个命名。
//注意函数内部的变量名称和调用函数时候的外部名称
func sayHello(to name:String , withGreetingWord greeting:String) -> String{
return("\(name),\(greeting)")
}
sayHello(to: "Erlich", withGreetingWord: "Good morning")
设置可以忽略的外部参数
但是还存在一个问题,当你不需要一个外部参数的时候,语法看上去就会显得很杂乱,所以可以选择在调用函数的时候省略参数名称。那么在定义函数参数的时候,在参数的前面添加一个下划线。
//添加一个下划线后的参数,再调用函数的时候就会省略掉参数的名称,默认按照参数定义的顺序赋值
func mutiple( _ num1: Int , _ num2: Int) -> Int{
return num1 * num2
}
mutiple(2, 3)
设置带有默认参数的函数
其实就是在原有的参数部分,给一个参数赋上一个值,那么当调用这个函数的时候,可以选择忽略掉这个值。这样其实可以很大部分的缩短要编写的不同类型的构造函数,有点像是 C#语言中的多态的概念。
func sayHello( _ name:String , withGreetingWord greeting:String = "Hello" , punctuation:String = "!") ->String{
return ("\(name),\(greeting)\(punctuation)")
}
sayHello("Erlich")
sayHello("Erlich", withGreetingWord: "你好", punctuation: "!")
sayHello("elrich", punctuation: "!")
设置变长的参数类型
swift 其实又为构造函数想了一部分,当你不知道参数有多少个,但是知道参数的类型的时候,那么就就可以才用变长的参数类型,其实变长的参数类型也是很简单的,就是在参数的后面加上 ...
。这样 swift 会把输入的参数当做一个数组来处理,这样就可以实现变长的参数处理了。但是一个函数只能有一个变长的参数。
//变长的参数类型,其实就是把变量当做数组,一个函数只能有一个变长类型
func mean(numbers:Double ... ) -> Double{
var sum:Double = 0
for number in numbers{
sum += number
}
return sum / Double(numbers.count)
}
mean(numbers: 1,2,3,4,5,6,7,8,8)
可变参数(在 swift3 中已经取消掉了,实际也不建议使用这样逻辑)
在 swift 中声明的参数其实默认都是 let
类型的,但是有的时候呢,是需要改变参数的,那么就直接在参数中显式的声明为avr
就可以了。其实在 swift3中已经取消掉了自定义参数类型,但是如果有需求,还是可以实现的
//在 swift3 中,已经不能再自定义参数类型了,但是可以在函数体内进行改变。
//这是一个简单的将十进制转换为二进制的算法
func toBinary( num:Int ) ->String{
var num = num
var res = ""
repeat{
res = String(num%2) + res
num /= 2
}while num != 0
return res
}
toBinary(num: 12)
inout 类型的参数
在 swift 中的参数,都是按引用传入的,当用inout
类型的参数时候,就可以实现按值传入了。
//这个函数是将数组里的值全部转换成 value 的值
func initArray( arr:inout [Int] , by value:Int){
for i in 0..<arr.count {
arr[i] = value
}
}
var array = [1,3,3,5,5]
initArray(arr: &array, by: 0)
array
将函数当做变量来使用
将函数参数化是高级函数的内容,将函数参数化可以实现更加灵活的功能,可以说函数参数化其实是在一个已知的函数上进行拓展的一种操作。
import Cocoa
//参数列表和返回值都不为空的状态
func add( _ a:Int , _ b:Int ) -> Int{
return a + b
}
let anotherAdd:(Int , Int) ->Int = add
anotherAdd(2,4)
//返回值为空的状态,当某个值为空的时候,可以使用空的()来表示,也可以使用 Void 来表示
func sayHelloTo( _ name:String){
print("Hello, \(name)")
}
let sayAnotherHelloTo:(String) -> Void = sayHelloTo
sayAnotherHelloTo("Erlich")
//参数列表和返回值都为空的状态
func sayHello(){
print("Hello")
}
//初次之外你还能想得到3种另外的搭配组合
let sayAnotherHello:()->() = sayHello
//对于参数式函数的应用场景
var arr:[Int] = []
for _ in 0...100 {
arr.append(Int(arc4random()%1000))
}
//对于数组排序,但是不改变原来数组的顺序
arr.sorted()
arr
//给数组排序,并改变原来数组的顺序
arr.sort()
arr
//目前存在的排序方式都不够灵活多变,不能满足需要,那么就要用到参数式函数了
//我们来自定义一个函数,通过参数式函数的方式来改造 sorted()函数
func biggerNumberFirst(_ a:Int , _ b: Int) -> Bool{
/*if a > b{
return true
}else{
return false
}*/
return a > b
}
//这时就实现了从大到小的排列的函数
arr.sorted(by: biggerNumberFirst)
//这样就实现了按照字符串的大小排列了
func cmpNumberByString( _ a:Int,_ b:Int)->Bool{
return String(a) < String(b)
}
arr.sorted(by: cmpNumberByString)
//这样就实现了谁离500更近的排序
func near500(_ a:Int,_ b:Int)->Bool{
return abs(500 - a) < abs(500 - b) ? true:false
}
arr.sorted(by: near500)
自己构造一个参数式函数
构造一个参数式函数的好处就是,当某个小的功能可能频繁的出现需求变更,可以不需要改变以前定义好的函数,只需要改变其中的一个当做参数的函数就可以了。swift 应该是用一种更加方便易用的方式来优化了构造函数的概念。
import Cocoa
//采用参数式函数的好处,就是在功能发生变化的时候,不需要改变每一个函数,只需要改写参数函数的一小部分
//scores 才用 inout 类型是为了执行完函数可以改变原有的数组结构,另一个参数是一个定义分数计算方式的函数
func changeScores( scores:inout [Int] , by changeScore:(Int) -> Int){
for (index , score) in scores.enumerated() {
scores[index] = changeScore(score)
}
}
//定义了一种分数计算的方式
func changeScore1(score:Int) -> Int{
return Int(sqrt(Double(score)) * 10)
}
//定义了另一种分数计算方式
func changeSocre2(score:Int) -> Int{
return Int(Double(score) / 150 * 100)
}
//定义了两组存着分数的数组
var scores1:[Int] = [30,48,27,40,94,69]
var scores2 = [30,45,6,6,72,45,89,90]
//采用第一种分数计算方式
changeScores(scores: &scores1, by: changeScore1)
//采用第另一种分数计算方式
changeScores(scores: &scores2, by: changeSocre2)
map、filter、reduce 三大函数的使用
map 可以帮助减少上面的代码,执行一些改变数组或者别的的逻辑操作。filter 可以简单地进行过滤操作,reduce 可以简单地进行聚合操作。
import Cocoa
var score = [50,60,28,70,80,90]
//用 map 操作来改变数组
func isPassOrFail(score:Int) -> String{
return score < 60 ? "Fail" : "Pass"
}
score.map(isPassOrFail)
//用 filter 操作来过滤数组
func fail(score:Int) -> Bool{
return score < 60
}
score.filter(fail)
//用 reduce 把数组的值聚合成一个值,最典型的就是求数组的值的和。当然,reduce 还可以聚集字符串
func add(_ num1:Int , _ num2:Int) -> Int{
return num1 + num2
}
score.reduce(0, add)
score.reduce(0, +)
函数的嵌套
import Cocoa
//第一种邮寄费用定价
func tier1MailFeeByWeight(weight:Int) -> Int{
return 1*weight
}
//第二种邮寄费用定价
func tier2MailFeeByWeight(weight:Int) -> Int{
return 3*weight
}
//计算本次邮寄费用
func feeByUnitPrice(price:Int , weight:Int) -> Int{
//判断选用那个邮寄费用定价
func chooseMailFeeCalculationByWeight(weight:Int) -> (Int) -> (Int){
return weight <= 10 ? tier1MailFeeByWeight : tier2MailFeeByWeight
}
let mailFeeByWeight = chooseMailFeeCalculationByWeight(weight:weight)
return mailFeeByWeight( weight ) + price * weight
}
feeByUnitPrice(price: 100, weight: 16)
闭包
闭包其实和 OC 语言的 block 或者其他语言的 lambda、匿名函数的概念是一样的。虽然闭包和函数是一样的,但是某些时候使用闭包会更加的便利。
某些函数在程序中只会使用一次,不具备复用性,那么声明一个函数就变得不太合适。
import Cocoa
var arr:[Int] = []
for _ in 0..<100{
arr.append(Int(arc4random()%1000))
}
//传统的创建一个函数来解决一次性问题
func biggeNumberFirst(_ a:Int , _ b:Int) -> Bool{
return a > b
}
arr.sorted(by: biggeNumberFirst)
//使用闭包函数,对于只使用一次的函数不需要再构建一个函数
arr.sorted(by: { (a:Int , b:Int) -> Bool in
return a < b
})
对于上面的代码进行优化,如果闭包函数的逻辑体只有一行,可以把整个闭包都写成一行。arr.sorted(by: { (a:Int , b:Int) -> Bool in return a < b})
还有很多的简化,虽然一路下来,一个闭包函数可以变得非常简化,但是并不建议一定使用最简单的办法,因为最简单的往往语义并不明确,可读性会变得很差。
import Cocoa
var arr:[Int] = []
for _ in 0..<100{
arr.append(Int(arc4random()%1000))
}
//使用闭包函数,对于只使用一次的函数不需要再构建一个函数
//arr.sorted(by: { (a:Int , b:Int) -> Bool in return a < b})
//它本身就是规定了传入的参数类型和返回类型,那么在简化的时候,可以直接填写参数就够了,连返回类型都不用
//arr.sorted(by: <#T##(Int, Int) -> Bool#>)
//swift 有智能类型推断,所以直接天上参数,逻辑语句就可以了,是不是非常简洁。
arr.sorted(by:{ a , b in return a < b })
//既然保证了一定会有返回值,那么连逻辑语句里的 return 都可以省略掉
arr.sorted(by:{ a , b in a > b })
//既然函数的参数都是我们自己命名的,但是函数其实自己也有命名,那么就可以这样写 $0 $1
arr.sorted(by: { $0 > $1 })
//sorted()函数本身就是在参数传入一个比较函数,那么还可以这样写
arr.sorted(by: > )
结尾闭包 Trailing Closure
当闭包函数是传入的最后一个参数时,那么就可以把闭包函数结构放在函数的小括号外,像是一个真正的函数。
这里的 sorted()函数本身就只有一个参数,所以很容易就可以变成这样的。只要保证闭包函数是一个方法参数里的最后一个参数,都可以变成专这样的形式arr.sorted(){ a , b in a > b }
内容捕获
本身一个变量并没有声明在闭包函数体内,但是闭包函数体可以使用。这就是闭包函数的内容捕获
import Cocoa
var arr:[Int] = []
for _ in 0..<100{
arr.append(Int(arc4random()%1000))
}
//本身变量并没有声明在闭包函数体内,但是闭包函数却可以正常调用。
var num = 200
arr.sorted(){ a,b in
abs(a - num) > abs(b - num)
}
闭包和函数是引用类型
闭包函数和函数都是引用类型,之前的数组,元组啊什么的都是值类型。如果是值类型,那么在做赋值操作的时候,完全是 Copy,而如果是引用类型,那在做赋值操作的时候其实会被执行赋值的函数逻辑,也就是会被再执行一遍。
import Cocoa
func runningMetersWithMetersPerDay( _ metersPerDay:Int) -> () -> Int{
var totalMeters = 0
return {
totalMeters += metersPerDay
return totalMeters
}
}
//声明 A 要调用一个方法,但是runningMetersWithMetersPerDay 这个方法返回的还是一个方法
//同时这个方法是一个无参数,返回值为 Int 的方法,那么其实planA()这个方法就是方法中的闭包函数的部分
//所以在调用 planA 的时候,还是要加上()来表示 planA 其实是要执行的是一个方法。
var planA = runningMetersWithMetersPerDay(2000)
planA()
planA()
var planB = runningMetersWithMetersPerDay(5000)
planB()
planB()
网友评论