三 字符串与字符
//1、字符串常量
//在代码中可以使用由String预先定义的字符串常量,定义方式非常简单:
let someString = "Some string literal value"
//字符串常量可以包括下面这些特殊字符:
//空字符\0,反斜杠\,制表符\t,换行符\n,回车符\r,双引号\”和单引号\’
//2、初始化一个空串
//初始化一个空串时有两种形式,但是两种初始化方法的结果都一样,表示空串
var emptyString = "" // empty string literal
var anotherEmptyString = String() // initializer syntax
// these two strings are both empty, and are equivalent to each other
//通过isEmpty属性可以检查一个字符串是否为空
if emptyString.isEmpty {
print("Nothing to see here")
}
//3、变长字符串
//如果使用var关键词定义的字符串即为可修改的变长字符串,而let关键词定义的字符串是常量字符串,不可修改。
var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"
let constantString = "Highlander"
//constantString += " and another Highlander"
// 上面那行报错,因为常量不可修改
//4、字符串不是指针,而是实际的值
//在Swift中,一个String类型就是一个实际的值,当定义一个新的String,并且将之前的String值拷贝过来的时候,是实际创建了一个相等的新值,而不是仅仅像指针那样指向过去。
//同样在函数传递参数的时候,也是传递的实际值,并且创建了一个新的字符串,后续的操作都不会改变原有的String字符串。
//5、字符
let yenSign: Character = "¥"
//6、使用字符串生成新串
//通过现有的字符串,可以使用如下方法来生成新的字符串:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
//7、字符串比较
//Swift提供三种方法比较字符串的值:字符串相等,前缀相等,和后缀相等
//字符串相等
//当两个字符串的包含完全相同的字符时,他们被判断为相等。
let quotation = "We‘re a lot alike, you and I."
let sameQuotation = "We‘re a lot alike, you and I."
if quotation == sameQuotation {
print("These two strings are considered equal")
}
//前缀(prefix)相等和后缀(hasSuffix)相等
//使用string 类的两个方法hasPrefix和hasSuffix,来检查一个字符串的前缀或者后缀是否包含另外一个字符串,它需要一个String类型型的参数以及返回一个布尔类型的值。两个方法都会在原始字符串和前缀字符串或者后缀字符串之间做字符与字符之间的。
//下面一个例子中,用一个字符串数组再现了莎士比亚的罗密欧与朱丽叶前两幕的场景
let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet‘s mansion",
"Act 1 Scene 3: A room in Capulet‘s mansion",
"Act 1 Scene 4: A street outside Capulet‘s mansion",
"Act 1 Scene 5: The Great Hall in Capulet‘s mansion",
"Act 2 Scene 1: Outside Capulet‘s mansion",
"Act 2 Scene 2: Capulet‘s orchard",
"Act 2 Scene 3: Outside Friar Lawrence‘s cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet‘s mansion",
"Act 2 Scene 6: Friar Lawrence‘s cell"
]
//你可以使用hasPrefix 方法和romeoAndJuliet数组 计算出第一幕要表演多少个场景。
var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
++act1SceneCount
}
}
print("There are \(act1SceneCount) scenes in Act 1")
//同理,使用hasSuffix 方法去计算有多少个场景发生在Capulet公馆和Friar Lawrence牢房
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet‘s mansion") {
++mansionCount
} else if scene.hasSuffix("Friar Lawrence‘s cell") {
++cellCount
}
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
//大小写字符串
//你可以从一个String类型的uppercaseString 和 lowercaseString中获得一个字符串的大写或小写
let normal = "Could you help me, please?"
let shouty = normal.uppercaseString
// shouty is equal to "COULD YOU HELP ME, PLEASE?"
let whispered = normal.lowercaseString
四 集合类型
//Swift 提供两种集合类型来存储集合,数组和字典。数组是一个同类型的序列化列表集合。字典是一个能够使用类似于键的唯一标识符来获取值的非序列化集合。
//在Swift中,数组和字典的键和值都必须明确它的类型。这意味这数组和字典不会插入一个错误的类型的值,以致于出错。这也意味着当你在数组和字典中取回数值的时候能够确定它的类型。
//Swift 使用确定的集合类型可以保证代码工作是不会出错,和让你在开发阶段就能更早的捕获错误。
//1、数组
//数组是储存同类型不同值的序列化列表。同样的值可以在数组的不同位置出现多次。
//Swift数组是储存确定的值,这个Objective-C中的NSArray和NSMutableArray类是有区别的。因为它们是储存各种的对象,而且并不提供返回任何有关对象的具体信息。在Swift中,无论是确定的声明,还是隐式的声明,数组是非常确定它自身是储存什么样的类型,而且,它并不一定要求储存的是类对象。所以Swift数组是类型安全的,因为它一直都确定它自身所能包含的值。
//数组的简略语法
//定义数组的完整写法是Array<SomeType>。其中SomeType是你想要包含的类型。你也可以使用类似于SomeType[]这样的简略语法。虽然这两种方法在功能上是相同的。但是我们更推荐后者。
//数组实量(Array Literals)
//你可以用一个数组实量(Array Literals)来初始化一个数组,它是用简略写法来创建一个包含一个或多个的值的数组。一个数组实量(Array Literals)是由它包含的值,“,”分隔符 已经包括以上内容的中括号对“[]”组成:
//[value1, value2]
//下面的例子创建一个叫shoppinglist,储存字符串(String)类型的数组。
var shoppingList: [String] = ["Eggs", "Milk"]
//注意
//shoppingList数组是被定义为一个变量(使用var 标识符)而不是常量(使用let 标识符),所以在下面的例子可以直接添加元素。
//得益于Swift的类型推断,当你用相同类型的值来初始化时,你可以不写明类型。初始化shoppingList可以用下面这个方法来代替。
var shoppingList1 = ["Eggs", "Milk"]
//因为数组实量(Array Literals)中所有的值都是同类型的,所以Swift能够推断shoppingList1的类型为字符串数组(String[])。
//读取和修改数组
//你可以通过方法和属性,或者下标来读取和修改数组。
//通过只读属性count来读取数组的长度;
print("The shopping list contains \(shoppingList.count) items.")
// prints "The shopping list contains 2 items.”
//通过一个返回布尔类型的isEmpty属性检查数组的长度是否为0
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// prints "The shopping list is not empty."
//在数组末尾增加一个元素可以通过append方法
shoppingList.append("Flour")
// shoppingList 现在包含3个元素
//你也可以用(+=)操作符来把一个数组添加到另一个数组的末尾
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
//从数组中取出一个值可以使用下标语法。如果你知道一个元素的索引值,你可以数组名后面的中括号中填写索引值来获取这个元素
var firstItem = shoppingList[0]
//你可以使用下标语法通过索引修改已经存在的值。
shoppingList[0] = "Six eggs"
//列表中的第一个值等于"Six eggs" 而不等于 “Eggs"
//你可以使用下标语法一次性改变一系列的值,尽管修改的区域远远大于要修改的值。
shoppingList[2...5] = ["Bananas", "Apples"]
print(shoppingList)
//在一个特定的索引位置插入一个值,可以使用insert(atIndex:)方法
shoppingList.insert("Maple Syrup", atIndex: 0)
//同理,你可以调用removeAtIndex方法移除特定的元素。这个方法移除特定索引位置的元素,已经返回这个被移除的元素(尽管你并不关心这个返回值)
let mapleSyrup = shoppingList.removeAtIndex(0)
//如果你从数组中移除最后一个元素,使用removeLast方法比removeAtIndex更方便,因为后者需要通过count属性计算数组的长度。和removeAtIndex方法一样,removeLast会返回被移除的元素。
let apples = shoppingList.removeLast()
//遍历
for item in shoppingList {
print(item)
}
//如果需要每一个元素的整形的索引值,使用enumerate函数代替会更方便,enumerate函数对于每一个元素都会返回一个包含元素的索引和值的元组(tuple)。你可以在遍历部分分解元祖并储存在临时变量或者常量中。
for (index, value) in shoppingList.enumerate() {
print("Item \(index + 1): \(value)")
}
//创建和初始化数组
//创建一个空的数组和确定的类型(不包含初始化值)使用的初始化语法:
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
//注意,someInt变量被确定为[Int],因为它使用生成[Int]的初始化方法。
//或者,如果上下文(context)已经提供类型信息,例如函数参数或者已经确定类型的常量和变量,你可以从空的数组实量(Array Literals)创建一个空数组,写作[]
someInts.append(3)
someInts = []
//Swift数组类型也提供初始化方法来创建确定长度和提供默认数值的数组。你可以通过这个初始化方法增加一个新的数组,元素的数量成为count,合适的默认值为repeatedValue
var threeDoubles = [Double](count: 3, repeatedValue: 0)
//得益于类型推断,你并不需要指明这个数组储存的类型就能使用这个初始化方法,因为它从默认值中就能推断出来。
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
//最后,你可以使用(+)操作符就能创建一个新的数组,把两个存在的数组添加进来
//这个新的数组类型从你添加的两个数组中推断出来
var sixDoubles = threeDoubles + anotherThreeDoubles
//2、字典
//Swift字典时储存一个类型的具体的键和值,和Objective-C的NSDictionary 和NSMutableDictionary由一定的区别,因为它们是使用各种的对象来作为它们的键和值,而且并不提供任何有关对象的具体信息。在Swift中,对于一个特定的字典,它所能储存的键和值都是确定的,无论是明确声明的类型还是隐式推断的类型。
//Swift的字典写法是Dictionary<KeyType,ValueType>,KeyType是你想要储存的键,ValueType是你想要储存的值。
//唯一的限制就是KeyType必须是可哈希的(hashable)——就是提供一个形式让它们自身是独立识别的。Swift的所有基础类型(例如字符串(String),整形(Int),双精度(Double)和布尔(Bool))在默认是可哈希的(hashable),和这些类型都常常用语当作字典的键。枚举成员值不需要协助值(associated values)(具体描述在 Enumerations)也是因为它们默认也是可哈希的(hashable)
//在下面的例子,将会创建一个字典来储存国际机场的名字。在这个字典里面,键是三个字的国际航空运送协会代码,以及它的值是机场的名称:
var 机场 :Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]
//注意
//机场字典是被定义为一个变量(使用var 标识符)而不是常量(使用let 标识符),所以可以直接添加元素。
//和数组一样,如果你初始化一个字典的时候使用相同的类型,你可以不指明字典的类型。
//airport初始化可以用下面这个简略写法来代替:
var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
//因为所有的键在字面上都是相同的类型,同样,所有的值也是同样的类型,所以Swift可以推断为Dictionary<String, String>是airports字典的正确类型。
//读取和修改字典
//你可以通过属性,方法或者下标来读取和修改字典。和数组一样,你使用只读的count属性来检查字典(Dictionary)包含多少个元素。
print("The dictionary of airports contains \(airports.count) items.")
// prints "The dictionary of airports contains 2 items."
//你可以使用下标语法给一个字典添加一个元素。使用合适类型作为新的键,并分配给它一个合适的值
airports["LHR"] = "London"
// airports dictionary 现在有 3 items
//你也可以使用下标语法去改变一个特定键所关联的值。
airports["LHR"] = "London Heathrow"
//updateValue(forKey:) 方法返回一个和字典的值相同类型的可选值. 例如,如果字典的值的类型时String,则会返回String? 或者叫“可选String“,这个可选值包含一个如果值发生更新的旧值和如果值不存在的nil值。
if let oldValue = airports.updateValue("Dublin International", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
//你也可以使用下标语法通过特定的键去读取一个值。因为如果他的值不存在的时候,可以返回他的键,字典的下标语法会返回一个字典的值的类型的可选值。如果字典中的键包含对应的值,这字典下标语法会返回这个键所对应的值,否则返回nil
if let airportName = airports["DB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
//你可以使用下标语法把他的值分配为nil,来移除这个键值对。
airports["APL"] = "Apple International"
// "Apple International" 不是 APL的真实机场,所以删除它
airports["APL"] = nil
// APL 已经从字典中被移除
//同样,从一个字典中移除一个键值对可以使用removeValueForKey方法,这个方法如果存在键所对应的值,则移除一个键值对,并返回被移除的值,否则返回nil。
if let removedValue = airports.removeValueForKey("DUB") {
print("The removed airport‘s name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
//遍历字典
//你可以使用一个for-in循环来遍历字典的键值对。字典中的每一个元素都会返回一个元祖(tuple),你可以在循环部分分解这个元祖,并用临时变量或者常量来储存它。
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
//你也可以读取字典的keys属性或者values属性来遍历这个字典的键或值的集合。
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
for airportName in airports.values {
print("Airport name: \(airportName)")
}
//创建一个空字典
//和字典一样,你可以使用确定类型的语法创建一个空的字典。
var namesOfIntegers = Dictionary<Int, String>()
// namesOfIntegers 是一个空的 Dictionary<Int, String> 类型的字典
//这个例子创建一个Int,String类型的字典来储存可读性较好的整数值。它的键是Int类型,以及它们的值是String类型
//如果 上下文(context )中已经提供类型信息,可用一个字典实量(Dictionary Literal)创建一个空的字典,写作[:](由一对[]包含一个冒号:)
namesOfIntegers[16] = "sixteen"
// namesOfIntegers现在包含1 个键值对
namesOfIntegers = [:]
五 控制流
//Swift提供了所有C语言中相似的控制流结构。包括for和while循环;if和switch条件语句;break和continue跳转语句等。
//Swift还加入了for-in循环语句,让编程人员可以在遍历数组,字典,范围,字符串或者其它序列时更加便捷。
//相对于C语言,Swift中switch语句的case语句后,不会自动跳转到下一个语句,这样就避免了C语言中因为忘记break而造成的错误。另外case语句可以匹配多种类型,包括数据范围,元组,或者特定的类型等。switch语句中已匹配的数值也可以被用在后续的case语句体中,where关键词还能被加入任意的case语句中,来增加匹配的方式。
//1 for循环
//for-in循环
for i in 1..<6 {
print(i)
}
//如果你不需要序列中的每一个值,可以使用_来忽略它,仅仅只是使用循环体本身
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
//For-Condition-Increment条件循环
//Swift同样支持C语言样式的for循环,它也包括了一个条件语句和一个增量语句:
for var index = 0; index < 3; ++index {
print("index is \(index)")
}
//2 while循环
//do-while不用了,repeat-while取代
var n = 2
while n < 100 {
n = n * 2
}
print(n)
var m = 2
repeat {
m = m * 3
} while m < 100
print(m)
//3、条件语句
//if语句差不多,知识条件可以不用括号括起来
//灵活的switch
//Swift中的switch支持各种各样的比较操作:
let vegetable = "blue pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
print(vegetableComment)
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
print(vegetableComment)
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
print(vegetableComment)
default:
let vegetableComment = "Everything tastes good in soup."
print(vegetableComment)
}
//在switch中,每一个case分支都会被匹配和检测到,所有case没有提到的情况都必须使用default关键词。注意default关键词必须在所有case的最后。
//跟C和Objective-C不同,Swift中的switch语句不会因为在case语句的结尾没有break就跳转到下一个case语句执行。switch语句只会执行匹配上的case里的语句,然后就会直接停止。这样可以让switch语句更加安全,因为很多时候编程人员都会忘记写break。
//case中还可以直接测试元组是否符合相应的条件,_可以匹配任意值。
//下面的例子是判断(x,y)是否在矩形中,元组类型是(Int,Int)
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("(0, 0) is at the origin")
case (_, 0):
print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
//数值绑定
//在case匹配的同时,可以将switch语句中的值绑定给一个特定的常量或者变量,以便在case的语句中使用。比如:
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
//Where关键词
//switch语句可以使用where关键词来增加判断的条件,在下面的例子中
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
//每个case都因为有where而不同,第一个case就是判断x是否与y相等,表示点在斜线y=x上。
//4、控制跳转语句
//在Swift中控制跳转语句有4种,让编程人员更好地控制代码的流转,包括:continue, break, fallthrough, return
//continue
//continue语句告诉一个循环停止现在在执行的语句,开始下一次循环。
//注意:在for-condition-increment循环中,increment增量语句依然执行,只是略过了一次循环体。
//下面的例子实现的是去除一个字符串中的空格和元音字母,从而组成一个字谜:
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput.characters {
switch character {
case "a", "e", "i", "o", "u", " ":
continue
default:
puzzleOutput.append(character)
}
}
print(puzzleOutput)
//遍历字符串的每一个字符,当遇到元音字母或者空格时就忽略,进行下一次循环,从而得到了最终的字谜。
//break
//break语句将终止整个循环的执行,可以用在循环语句中,也可以用在switch语句中。
//fallthrough
//由于Swift中的switch语句不会自动的因为没有break而跳转到下一个case,因此如果需要想C语言中那样,依次执行每个case的时候,就需要用到fallthrough关键词。
//像下面这个例子一样,default分支最终都会被执行:
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
print(description)
//标签语句
//switch和循环可以互相嵌套,循环之间也可以互相嵌套,因此在使用break或者continue的时候,需要知道到底是对哪个语句起作用。这就需要用到标签语句
//下面的例子演示了如何使用标签语句以及嵌套的循环和switch。
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0
gameLoop: while square != finalSquare {
if ++diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// diceRoll will move us to the final square, so the game is over
break gameLoop
case let newSquare where newSquare > finalSquare:
// diceRoll will move us beyond the final square, so roll again
continue gameLoop
default:
// this is a valid move, so find out its effect
square += diceRoll
square += board[square]
}
}
print("Game over!")
//在这个代码中,将游戏的循环命名为gameLoop,然后在每一步移动格子时,判断当前是否到达了游戏终点,在break的时候,需要将整个游戏循环终止掉,而不是终止switch,因此用到了break gameLoop。同样的,在第二个分支中,continue gameLoop也指明了需要continue的是整个游戏,而不是switch语句本身。