你對(duì)Swift中的元組了解多少呢?
...
很有自信嘛
...
看完這篇文章再說(shuō)嘍
元組
- 元組是Swift中特有的,OC中并沒(méi)有相關(guān)數(shù)據(jù)類型
- 元組是一種數(shù)據(jù)結(jié)構(gòu),它可以把多個(gè)值,組成一個(gè)復(fù)合值,元組內(nèi)元素的類型無(wú)需相同
元組的定義格式:
let 元組名稱 = (元素,元素...)
數(shù)組?字典?元組!
- 使用數(shù)組定義的數(shù)據(jù)
缺點(diǎn):在一個(gè)數(shù)組中,如果存在多種數(shù)據(jù)類型,所有的元素類型都將會(huì)一并變?yōu)?strong>NSObject
例:
let infoArray = ["lyu" , 18]
let name = infoArray[0] //缺點(diǎn):name的類型是NSObject,而不是String,也就是說(shuō)我們是沒(méi)有辦法敲出name.characters這個(gè)方法的
let length = name.characters.count //編譯報(bào)錯(cuò)
- 使用字典定義的數(shù)據(jù)
缺點(diǎn):在一個(gè)字典中,如果其value值存在多種數(shù)據(jù)類型,所有的value類型都將會(huì)一并變?yōu)?strong>NSObject
例:
let infoDic = ["name" : "lyu" , "age" : 18]
let name = infoDic["name"] //缺點(diǎn):name的類型是NSObject,而不是String,也就是說(shuō)我們是沒(méi)有辦法敲出name.characters這個(gè)方法的
let length = name.characters.count //編譯報(bào)錯(cuò)
- 使用元組來(lái)定義數(shù)據(jù):
優(yōu)點(diǎn):元組中的數(shù)據(jù)是按照他們的真實(shí)類型來(lái)存儲(chǔ)的
例:
let infoTuple = ("lyu" , 18) //元組會(huì)為他的元素儲(chǔ)存其真實(shí)類型
let name = infoTuple.0 //name的類型為String
let length = name.characters.count //毫無(wú)壓力的拿到字符串的長(zhǎng)度
元組的使用方法
- 給元組中的元素起別名
例:
let errorTuple = (newName : "Not Found" , newCode : 404)
let errorName = errorTuple.newName
let errorCode = errorTuple.newCode
//當(dāng)然.0和.1仍然可以取值
let errorName1 = errorTuple.0
let errorCode1 = errorTuple.1
- 給元組起別名的另一種方法:
要注意的是,通過(guò)這種方法來(lái)創(chuàng)建元組是拿不到元組名字的
缺點(diǎn):拿不到元組的名字
優(yōu)點(diǎn):快速解構(gòu)
例:
let (newName , newCode) = ("Not Found" , 404)
let errorName = newName
let errorCode = newCode
提示:這種方法來(lái)創(chuàng)建的元組相當(dāng)于直接創(chuàng)建了兩個(gè)常量
元組的進(jìn)階使用方法
- 與Switch搭配進(jìn)行復(fù)雜條件的判斷:
例:招聘一個(gè)20歲以下的員工,并同時(shí)打印其個(gè)人信息
let name = "lyu" //姓名
let age = 18 //年齡
var personalInfo = [String : NSObject]() //個(gè)人信息
personalInfo["height"] = 1.80 //個(gè)人信息
switch (age,name,personalInfo) { //包裝元組,元組中元素的命名要與姓名,年齡,個(gè)人信息完全一致
case (let age , let name , let personalInfo) where age < 20: //滿足age<20則執(zhí)行case
print(age)
print(name)
print(personalInfo)
default:
print(age)
}
- 作為函數(shù)的返回值,來(lái)創(chuàng)建多個(gè)返回值的函數(shù)
例:計(jì)算一個(gè)數(shù)組中奇數(shù)偶數(shù)的值,一并返回
func getCount(nums : [Int]) -> (Int , Int) { //將返回值構(gòu)建為一個(gè)元組
var oddCount = 0 //初始化偶數(shù)個(gè)數(shù)
var evenCount = 0 //初始化奇數(shù)個(gè)數(shù)
for num in nums { //遍歷數(shù)組
if num % 2 == 0 { 判斷是否為偶數(shù)
oddCount += 1
}
else //反之為奇數(shù)
{
evenCount += 1
}
}
return (oddCount , evenCount) 返回元組:(偶數(shù),奇數(shù))
}
let nums = [12,14,15,2,77,13]
let counts = getCount(nums)
print(counts)
print(counts.0)
print(counts.1)
- 函數(shù)作為元組的元素,進(jìn)行多函數(shù)同時(shí)調(diào)用
例:
func test1() -> String{
return "test1"
}
func test2() -> String{
return "test2"
}
func test3() -> String{
return "test3"
}
let funcTuple = (a : test1() , b : test2() , c : test3())
print(funcTuple)
-
交換值
被交換的必須是變量,并且這兩個(gè)變量類型必須相同
例:
var (x , y) = (11 , 22)
(x , y) = (y , x)
print(x , y)
元組的超進(jìn)階使用方法
- 以假亂真:使用元組代替結(jié)構(gòu)體
例:
//定義結(jié)構(gòu)體
struct newS {
var name : String
var age : Int
}
let temp = newS(name : "lyu" , age :18)
//定義元組
let tuple = (name : "lyu" , age : 18)
//使用元組和結(jié)構(gòu)體
print(tuple.name)
print(temp.name)
Tips:結(jié)構(gòu)體?元組?
我們發(fā)現(xiàn)這兩個(gè)家伙還是有所不同的:元組并沒(méi)有結(jié)構(gòu)體的聲明部分,所以如果只是臨時(shí)使用,或臨時(shí)拼湊一個(gè)結(jié)構(gòu)體,那么建議使用元組
如果你的需求超出了"臨時(shí)"(根據(jù)你的代碼自己判斷嘍~)的范圍,那么還是建議將數(shù)據(jù)封裝成結(jié)構(gòu)體
另外:也可以使用元組來(lái)代替"匿名結(jié)構(gòu)體哦"
- 處理數(shù)據(jù)的過(guò)程中,將某一個(gè)函數(shù)的返回值作為臨數(shù)據(jù)傳入另一方法中
- 元組可以作為函數(shù)的參數(shù)
- 元組也可以作為函數(shù)的返回值,既然如此,這種用法當(dāng)然也可以成立了
//元組作為函數(shù)返回值
func getViewInfo() -> (r : Int , alpha : Double , location : (Double , Double)){
return (255 , 0.5 , (100 , 100))
}
//元組作為函數(shù)參數(shù)
func getAlpha(tuple : (r : Int , alpha : Double , location : (Double , Double))) -> Double{
return tuple.alpha
}
let alpha = getAlpha(getViewInfo())
print(alpha)
- 當(dāng)然根據(jù)上一條使用方法,我們也可以想到本例也可以使用結(jié)構(gòu)體來(lái)實(shí)現(xiàn),如下:
//聲明結(jié)構(gòu)體
struct Location {
var x : Double
var y : Double
}
struct Info {
var r : Int
var alpha : Double
var location : Location
}
//定義函數(shù)
func getViewInfo() -> (Info){
return Info(r: 255 , alpha: 0.5 , location: Location(x: 100 , y: 100))
}
func getAlpha(stc : Info) -> Double{
return stc.alpha
}
//調(diào)用函數(shù)
let alpha = getAlpha(getViewInfo())
print(alpha)
tips:
為了兩個(gè)函數(shù)而刻意定義兩個(gè)結(jié)構(gòu)體,這種做法顯然太浪費(fèi)了,所以這里才為大家介紹了上面元組代替結(jié)構(gòu)體的方法
- 具體定義元組類型
//typealias相當(dāng)于C/OC中的typedef,用于起別名
typealias Tuple = (name : String , age : Int ,height : Double) //這與結(jié)構(gòu)體的聲明及其相似
func printTuple(tempTuple : Tuple){ //使用Tuple類型定義形參
print(tempTuple)
}
//下面提供三種用法
printTuple((name: "lyu" , age : 18 , height : 1.80))
printTuple(Tuple("lyu" , 18 , 1.80))
printTuple(("lyu" , 18 , 1.80))
- 約束一個(gè)類型元素的個(gè)數(shù)
當(dāng)我們創(chuàng)建一個(gè)數(shù)組可變數(shù)組,并且希望這個(gè)數(shù)組未來(lái)存儲(chǔ)7組數(shù)據(jù),例如統(tǒng)計(jì)一周內(nèi)的每日降雨量
//做法1:我們首先想到使用一個(gè)數(shù)組來(lái)包裝這7天的數(shù)據(jù)
var info = [Int]()
info.append(11)
//缺點(diǎn):這種做法沒(méi)辦法控制數(shù)組中元素的數(shù)量,如果外界添加元素的時(shí)候不小心添加了8個(gè),就沒(méi)辦法按地球的邏輯解釋了,難道真的有星期八~
//做法2:使用元組包裝數(shù)據(jù)
var info : (Int,Int,Int,Int,Int,Int,Int,Int) //什么?不是7個(gè),一定是你擼多眼花了
//優(yōu)點(diǎn):這樣做可以把info中的元素個(gè)數(shù)約束為7個(gè),多于少于7都會(huì)報(bào)錯(cuò)
- 作為函數(shù)的可變參數(shù)
當(dāng)函數(shù)的參數(shù)數(shù)量不確定的時(shí)候
例:
func sum(numbers : Int...) -> Int{
return numbers.reduce(0, combine: +) //第一個(gè)參數(shù)為基值,代表在0的基礎(chǔ)上累加
}
let result = sum(1,2,3)
- 元組與泛型
元組中元素的真實(shí)類型是根據(jù)元組中元素類型來(lái)確定的,所以與其讓泛型來(lái)約束元組,還不如根據(jù)我們的需求來(lái)定義一個(gè)明確數(shù)據(jù)類型的元組,反過(guò)來(lái)控制泛型- 由元組的類型來(lái)指定函數(shù)的泛型
//例:元組與泛型與函數(shù)
func three<c1 , c2 , c3>(tuple : (c1 , c2 , c3)) -> c3{ //利用泛型來(lái)聲明元組屬性類型,此時(shí)元組的屬性類型與泛型都還是不確定的
return tuple.2
}
//隨便傳入一個(gè)元組,均可成功調(diào)用該函數(shù)
let height = three(("Lyu" , 18 , 1.88)) //調(diào)用three函數(shù)的那一時(shí)刻,Swift根據(jù)參數(shù)(元組)類型推導(dǎo)出three的泛型!此時(shí)拿到的height是明確的Double類型
- 根據(jù)需求,由子類來(lái)指定父類泛型
例:元組與泛型與類
class superClass<c1 , c2 , c3>{ //定義父類,此時(shí)泛型不確定
typealias numbers = (c1 , c2 , c3) //利用此時(shí)不確定的泛型來(lái)聲明一個(gè)元組
func printNewNumbers(nums : numbers) -> Void {
print(nums)
}
}
class childClass<c1 , c2 , c3> : superClass<c1 , c2 , c3> { //定義子類,此時(shí)父類的泛型與子類相同,但子類的泛型不確定
}
let child = childClass<String , Int , Double>() //實(shí)例化對(duì)象,我們可以在此處根據(jù)需求來(lái)確定泛型,此時(shí)父類與子類泛型均以確定,
child.printNewNumbers(("lyu" , 18 , 1.80)) //創(chuàng)建元組作為參數(shù)
//當(dāng)然我們也可以這樣寫
class superClass<c1 , c2 , c3>{
typealias numbers = (c1 , c2 , c3)
func printNewNumbers(nums : numbers) -> Void {
print(nums)
}
}
class childClass<c1 , c2> : superClass<c1 , c2 , Double> { //在此處確定元組中某一個(gè)元素的類型(當(dāng)然,前提你真的確定這個(gè)元素的類型...)
}
let child = childClass<String , Int>() //這時(shí),我們只需要確定前兩個(gè)元素的類型即可
child.printNewNumbers(("lyu" , 18 , 1.80))
tips:
當(dāng)我們需要封裝一個(gè)函數(shù),但卻不明確要傳入的參數(shù)(不只是元組),可以利用這種思想,舉一個(gè)最簡(jiǎn)單的例子如下:
func myNameOrAge<c1>(nameOrAge : c1) -> c1{
return nameOrAge
}
let name = myNameOrAge("Lyu") //這里name的類型是String而不是可選類型,也不是anyObject
let age = myNameOrAge(18) //這里age的類型是Int而不是可選類型,也不是anyObject
也就是說(shuō):此時(shí)反過(guò)來(lái)看return nameOrAge這一句中已經(jīng)拿到了nameOrAge的真是類型,假如我們?cè)诤瘮?shù)體內(nèi)做一些復(fù)雜的運(yùn)算,最終返回的值仍然是一個(gè)已知的類型,是不是比anyObject要好的多呢
- 元組作為函數(shù)的參數(shù)
- 利用有參元組,作為帶外部參數(shù)函數(shù)的參數(shù)
func sum(a a : Int , b : Int , hello : String) -> Int {
return a + b
}
let tuple = (a : 1,b : 2, hello : "hello") //利用有參元組作為帶外部參數(shù)的函數(shù)的參數(shù)
let result = sum(tuple)
- 利用無(wú)參元組,作為不帶外部參數(shù)函數(shù)的參數(shù)
func sum(a : Int , _ b : Int , _ hello : String) -> Int {
return a + b
}
let tuple = (1 , 2 , "hello") //利用無(wú)參元組作為不帶外部參數(shù)函數(shù)的參數(shù)
let result = sum(tuple)
- 一個(gè)函數(shù)返回的元組作為另一個(gè)函數(shù)的參數(shù)
func getTuple() -> (Int , Int , String){
return(1, 2, "hello")
}
func printTuple(tuple : (Int , Int , String)){
print(tuple)
}
printTuple(getTuple())
//這種傳遞方式可以讓我們無(wú)需結(jié)構(gòu)元組(返回值),即可完成多個(gè)數(shù)據(jù)的傳遞