更新 Swift Tour 一章某些未和2.0版本同步的地方

更新 Swift Tour 一章某些未和2.0版本同步的地方
This commit is contained in:
semper_idem
2015-09-17 19:40:57 +08:00
parent 02fe0f4820
commit 789d05a9e6

View File

@ -18,10 +18,10 @@
通常来说编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现: 通常来说编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
```swift ```swift
println("Hello, world") print("Hello, world!")
``` ```
如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main`函数。你同样不需要在每个语句结尾写上分号。 如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main()`函数。你同样不需要在每个语句结尾写上分号。
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。 这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
@ -80,9 +80,7 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
```swift ```swift
var shoppingList = ["catfish", "water", "tulips", "blue paint"] var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water" shoppingList[1] = "bottle of water"
```
```swift
var occupations = [ var occupations = [
"Malcolm": "Captain", "Malcolm": "Captain",
"Kaylee": "Mechanic", "Kaylee": "Mechanic",
@ -107,7 +105,7 @@ occupations = [:]
<a name="control_flow"></a> <a name="control_flow"></a>
## 控制流 ## 控制流
使用`if``switch`来进行条件操作,使用`for-in``for``while``do-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。 使用`if``switch`来进行条件操作,使用`for-in``for``while``repeat-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
```swift ```swift
let individualScores = [75, 43, 103, 87, 12] let individualScores = [75, 43, 103, 87, 12]
@ -119,7 +117,7 @@ for score in individualScores {
teamScore += 1 teamScore += 1
} }
} }
teamScore print(teamScore)
``` ```
`if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。 `if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。
@ -128,8 +126,8 @@ teamScore
```swift ```swift
var optionalString: String? = "Hello" var optionalString: String? = "Hello"
optionalString == nil print(optionalString == nil)
var optionalName: String? = "John Appleseed" var optionalName: String? = "John Appleseed"
var greeting = "Hello!" var greeting = "Hello!"
if let name = optionalName { if let name = optionalName {
@ -148,19 +146,21 @@ if let name = optionalName {
let vegetable = "red pepper" let vegetable = "red pepper"
switch vegetable { switch vegetable {
case "celery": case "celery":
let vegetableComment = "Add some raisins and make ants on a log." print("Add some raisins and make ants on a log.")
case "cucumber", "watercress": case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich." print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"): case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?" print("Is it a spicy \(x)?")
default: default:
let vegetableComment = "Everything tastes good in soup." print("Everything tastes good in soup.")
} }
``` ```
> 练习: > 练习:
> 删除`default`语句,看看会有什么错误? > 删除`default`语句,看看会有什么错误?
注意`let`在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量`x`
运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break` 运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break`
你可以使用`for-in`来遍历字典,需要两个变量来表示每个键值对。字典是一个无序的集合,所以他们的键和值以任意顺序迭代结束。 你可以使用`for-in`来遍历字典,需要两个变量来表示每个键值对。字典是一个无序的集合,所以他们的键和值以任意顺序迭代结束。
@ -179,7 +179,7 @@ for (kind, numbers) in interestingNumbers {
} }
} }
} }
largest print(largest)
``` ```
> 练习: > 练习:
@ -192,13 +192,13 @@ var n = 2
while n < 100 { while n < 100 {
n = n * 2 n = n * 2
} }
n print(n)
var m = 2 var m = 2
do { repeat {
m = m * 2 m = m * 2
} while m < 100 } while m < 100
m print(m)
``` ```
你可以在循环中使用`..<`来表示范围,也可以使用传统的写法,两者是等价的: 你可以在循环中使用`..<`来表示范围,也可以使用传统的写法,两者是等价的:
@ -208,13 +208,13 @@ var firstForLoop = 0
for i in 0..<4 { for i in 0..<4 {
firstForLoop += i firstForLoop += i
} }
firstForLoop print(firstForLoop)
var secondForLoop = 0 var secondForLoop = 0
for var i = 0; i < 4; ++i { for var i = 0; i < 4; ++i {
secondForLoop += i secondForLoop += i
} }
secondForLoop print(secondForLoop)
``` ```
使用`..<`创建的范围不包含上界,如果想包含的话需要使用`...` 使用`..<`创建的范围不包含上界,如果想包含的话需要使用`...`
@ -254,8 +254,8 @@ func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
return (min, max, sum) return (min, max, sum)
} }
let statistics = calculateStatistics([5, 3, 100, 3, 9]) let statistics = calculateStatistics([5, 3, 100, 3, 9])
statistics.sum print(statistics.sum)
statistics.2 print(statistics.2)
``` ```
函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式: 函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式:
@ -337,14 +337,14 @@ numbers.map({
```swift ```swift
let mappedNumbers = numbers.map({ number in 3 * number }) let mappedNumbers = numbers.map({ number in 3 * number })
mappedNumbers print(mappedNumbers)
``` ```
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。 你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
```swift ```swift
let sortedNumbers = sorted(numbers) { $0 > $1 } let sortedNumbers = sorted(numbers) { $0 > $1 }
sortedNumbers print(sortedNumbers)
``` ```
<a name="objects_and_classes"></a> <a name="objects_and_classes"></a>
@ -436,12 +436,12 @@ class EquilateralTriangle: NamedShape {
} }
var perimeter: Double { var perimeter: Double {
get { get {
return 3.0 * sideLength return 3.0 * sideLength
} }
set { set {
sideLength = newValue / 3.0 sideLength = newValue / 3.0
} }
} }
override func simpleDescription() -> String { override func simpleDescription() -> String {
@ -449,9 +449,9 @@ class EquilateralTriangle: NamedShape {
} }
} }
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle") var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter print(triangle.perimeter)
triangle.perimeter = 9.9 triangle.perimeter = 9.9
triangle.sideLength print(triangle.sideLength)
``` ```
`perimeter`的 setter 中,新值的名字是`newValue`。你可以在`set`之后显式的设置一个名字。 `perimeter`的 setter 中,新值的名字是`newValue`。你可以在`set`之后显式的设置一个名字。
@ -469,14 +469,14 @@ triangle.sideLength
```swift ```swift
class TriangleAndSquare { class TriangleAndSquare {
var triangle: EquilateralTriangle { var triangle: EquilateralTriangle {
willSet { willSet {
square.sideLength = newValue.sideLength square.sideLength = newValue.sideLength
} }
} }
var square: Square { var square: Square {
willSet { willSet {
triangle.sideLength = newValue.sideLength triangle.sideLength = newValue.sideLength
} }
} }
init(size: Double, name: String) { init(size: Double, name: String) {
square = Square(sideLength: size, name: name) square = Square(sideLength: size, name: name)
@ -484,23 +484,10 @@ class TriangleAndSquare {
} }
} }
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape") var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength print(triangleAndSquare.square.sideLength)
triangleAndSquare.triangle.sideLength print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square") triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength print(triangleAndSquare.triangle.sideLength)
```
类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。
```swift
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
``` ```
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil``?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。 处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil``?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
@ -531,20 +518,20 @@ enum Rank: Int {
case .King: case .King:
return "king" return "king"
default: default:
return String(self.rawValue()) return String(self.rawValue)
} }
} }
} }
let ace = Rank.Ace let ace = Rank.Ace
let aceRawValue = ace.rawValue() let aceRawValue = ace.rawValue
``` ```
> 练习: > 练习:
> 写一个函数,通过比较它们的原始值来比较两个`Rank`值。 > 写一个函数,通过比较它们的原始值来比较两个`Rank`值。
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。 在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。
使用'rawValue'在原始值和枚举值之间进行转换。 使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。
```swift ```swift
if let convertedRank = Rank(rawValue: 3) { if let convertedRank = Rank(rawValue: 3) {
@ -569,7 +556,6 @@ enum Suit {
return "clubs" return "clubs"
} }
} }
} }
let hearts = Suit.Hearts let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription() let heartsDescription = hearts.simpleDescription()
@ -607,10 +593,10 @@ enum ServerResponse {
case Result(String, String) case Result(String, String)
case Error(String) case Error(String)
} }
let success = ServerResponse.Result("6:00 am", "8:09 pm") let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.") let failure = ServerResponse.Error("Out of cheese.")
switch success { switch success {
case let .Result(sunrise, sunset): case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)." let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
@ -671,13 +657,13 @@ let bDescription = b.simpleDescription
```swift ```swift
extension Int: ExampleProtocol { extension Int: ExampleProtocol {
var simpleDescription: String { var simpleDescription: String {
return "The number \(self)" return "The number \(self)"
} }
mutating func adjust() { mutating func adjust() {
self += 42 self += 42
} }
} }
7.simpleDescription print(7.simpleDescription)
``` ```
> 练习: > 练习:
@ -687,8 +673,8 @@ extension Int: ExampleProtocol {
```swift ```swift
let protocolValue: ExampleProtocol = a let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription print(protocolValue.simpleDescription)
// protocolValue.anotherProperty // Uncomment to see the error // print(protocolValue.anotherProperty) // Uncomment to see the error
``` ```
即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。 即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。
@ -699,32 +685,32 @@ protocolValue.simpleDescription
在尖括号里写一个名字来创建一个泛型函数或者类型。 在尖括号里写一个名字来创建一个泛型函数或者类型。
```swift ```swift
func repeat<ItemType>(item: ItemType, times: Int) -> [ItemType] { func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [ItemType]() var result = [Item]()
for i in 0..<times { for _ in 0..<numberOfTimes {
result.append(item) result.append(item)
} }
return result return result
} }
repeat("knock", 4) repeatItem("knock", numberOfTimes:4)
``` ```
你也可以创建泛型类、枚举和结构体。 你也可以创建泛型类、枚举和结构体。
```swift ```swift
// Reimplement the Swift standard library's optional type // Reimplement the Swift standard library's optional type
enum OptionalValue<T> { enum OptionalValue<Wrapped> {
case None case None
case Some(T) case Some(Wrapped)
} }
var possibleInteger: OptionalValue<Int> = .None var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100) possibleInteger = .Some(100)
``` ```
在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类 在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
```swift ```swift
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool { func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
for lhsItem in lhs { for lhsItem in lhs {
for rhsItem in rhs { for rhsItem in rhs {
if lhsItem == rhsItem { if lhsItem == rhsItem {
@ -740,4 +726,4 @@ anyCommonElements([1, 2, 3], [3])
> 练习: > 练习:
> 修改`anyCommonElements`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。 > 修改`anyCommonElements`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
简单起见,你可以忽略`where`,只在冒号后面写协议或者类名。` <T: Equatable>``<T where T: Equatable>`是等价的。 ` <T: Equatable>``<T where T: Equatable>`是等价的。