通俗地说,当一个东西在循环中运行时,它就会重复相同的事情。例如,一个循环就是迭代博客文章的数量并在主页面上显示它们。
Swift中的控制流有不同类型的循环。这些是for-in 、forEach 、while 、repeat-while 循环。在这篇文章中,我们将通过Swift中for-in 循环的基本概述。然后,我们将用不同数据类型的例子和用例来演示如何使用它们。
我们将重点介绍以下内容。
for-in循环的语法- 数组
- 范围和跨度
- 字典
- 枚举
要继续学习,你应该有Swift语言的基本知识。
for-in 循环的语法
语法以单词for 开始,然后是作为常量创建的循环中的特定元素。接着是单词in ,最后是你想循环的序列。
for element in elements {
// do something with the element
}
例如,我们有一个股票列表,每个股票包括其价格,在一个特定的日期。
struct Stock {
var name: String
var price: Double
var date = Date()
}
我们想在这个数组上循环,并打印每只股票的数据。循环的语法是这样的。
// MARK: - EXAMPLE
func printDetails(for stocks: [Stock]) {
for stock in stocks {
print(stock.name)
print(stock.price)
print(stock.date)
}
}
// MARK: - USAGE
let stocks = [Stock(name: "Banana", price: 125),
Stock(name: "TapeBook", price: 320),
Stock(name: "Ramalon", price: 3200)]
printDetails(for: stocks)
// MARK: - OUTPUT
Banana
125.0
2021-05-21 22:40:42 +0000
TapeBook
320.0
2021-05-21 22:40:42 +0000
Ramalon
3200.0
2021-05-21 22:40:42 +0000
有了基本语法的知识,让我们继续循环基本数据结构:Array!
数组
来自Swift官方文档,"一个数组在一个有序的列表中存储相同类型的值。同一个值可以在数组中的不同位置多次出现。"
我们使用for-in 循环来迭代存储的值,然后访问数组中的每个值。
基本例子
假设一个应用程序,我们正在跟踪一个用户的慢跑。在每个地点,我们都想跟踪他们的速度。因此,在应用程序中,我们收到一个位置数组。
let locations: [CLLocation] = []
我们循环浏览这个数组,对于每个地点,我们打印该特定地点的速度。
for location in locations {
print("The speed at location (\(location.coordinate.latitude), \(location.coordinate.longitude) is \(location.speed)")
}
再举个例子,我们创建一个10×10的二维数组,打印每个点的数值。
var board: [[Int]] = Array(repeating: Array(repeating: 0, count: 10), count: 10)
for row in board {
for number in row {
// prints 0, hundred times
print(number)
}
}
使用where 子句
有些情况下,我们想把序列只限制在符合特定条件的元素上。在这种情况下,我们使用where 关键字。
在一个待办事项应用程序中,我们需要所有目标中已完成目标的子集。假设有这样一个模型。
struct Goal: Identifiable, Hashable {
var id = UUID()
var name: String = "Goal Name"
var date = Date()
var goalCompleted: Bool = false
}
而我们的应用程序有一个数组,用于Goal 。我们想循环浏览这个数组,只访问那些已经完成的目标。
// MARK: - EXAMPLE
func getCompletedGoals(for goals: [Goal]) {
for goal in goals where goal.goalCompleted == true {
/// Access to completed goals only.
print(goal)
}
}
// MARK: - USAGE
let goals = [Goal(name: "Learn basic syntax of for-in loops", goalCompleted: true),
Goal(name: "Read about for-in loops and dictionaries"),
Goal(name: "Read about for-in loops and enums")]
getCompletedGoals(for: goals)
// MARK: - OUTPUT
Goal(id: B7B148D6-853B-486A-8407-CD03A904B348, name: "Learn basic syntax of for-in loops", date: 2021-05-21 22:50:38 +0000, goalCompleted: true)
使用enumerated()
要同时访问每个元素的索引,我们可以使用实例方法enumerated() 。它返回一个包含索引以及元素值的对的序列。以前面的例子为例,如果我们想列出数组中位置的索引,我们可以这样写。
for (index, location) in locations.enumerated() {
print("The speed at location (\(location.coordinate.latitude), \(location.coordinate.longitude) is \(location.speed)")
print("The index for this location is \(index)")
}
使用indices
如果我们只想要数组中元素的索引,我们可以使用indices 。这表示数组中的有效索引以升序排列。它从0循环到数组中的最后一个元素,即:array.count 。
for index in array.indices {
// Access the index
}
使用我们之前创建的二维数组,我们遍历每个点,并给它分配一个随机的整数值。
// MARK: - EXAMPLE
func updateValues(of board: inout [[Int]]) {
for rowIndex in board.indices {
for columnIndex in board[0].indices {
board\[rowIndex\][columnIndex] = Int.random(in: 0..<10)
}
print(board[rowIndex])
}
}
// MARK: - USAGE
var board: [[Int]] = Array(repeating: Array(repeating: 0, count: 10), count: 10)
updateValues(of: &board)
// MARK: - OUTPUT
[9, 4, 1, 7, 5, 2, 6, 4, 7, 4]
[1, 0, 1, 0, 5, 4, 5, 6, 7, 9]
[4, 7, 6, 3, 8, 9, 3, 5, 9, 5]
[8, 0, 9, 9, 6, 1, 2, 0, 2, 7]
[3, 7, 4, 1, 3, 4, 9, 9, 5, 6]
[5, 2, 5, 1, 8, 1, 8, 0, 0, 1]
[0, 4, 3, 4, 0, 6, 1, 8, 7, 5]
[7, 7, 7, 9, 1, 3, 6, 4, 0, 1]
[9, 5, 6, 5, 3, 8, 0, 1, 3, 4]
[1, 7, 7, 3, 1, 0, 7, 4, 5, 6]
使用一个可选模式
在序列包含可选值的情况下,我们可以使用for case let ,过滤掉nil值,只对非nil元素执行循环。
从前面的待办事项应用的例子来看,我们假设一些目标没有值。getCompletedGoals(for goals:) ,现在接受一个数组,可选择Goal 。
// MARK: - EXAMPLE
func getCompletedGoals(for goals: [Goal?]) {
for case let goal? in goals where goal.goalCompleted == false {
/// Access to completed goals only.
print(goal)
}
}
// MARK: - USAGE
let goals: [Goal?] = [Goal(name: "Learn something new!", goalCompleted: true),
Goal(name: "Read about for-in loops and dictionaries"),
nil,
Goal(name: "Read about for-in loops and enums"),
nil]
getCompletedGoals(for: goals)
// MARK: - OUTPUT
Goal(id: F6CB6D77-9047-4155-99F9-24F6D178AC2B, name: "Read about for-in loops and dictionaries", date: 2021-05-21 23:04:58 +0000, goalCompleted: false)
Goal(id: 822CB7C6-301C-47CE-AFEE-4B17A10EE5DC, name: "Read about for-in loops and enums", date: 2021-05-21 23:04:58 +0000, goalCompleted: false)
范围和stride
我们也可以使用for-in 循环来循环处理硬编码的数字范围。它们可以分为两部分。
- 使用一个封闭的范围操作符 (
…) - 使用半开范围运算符 (
..<)
使用闭合范围运算符
闭合范围运算符创建一个包括两个末端元素的范围。使用该运算符的一个基本例子是打印10个数字。这里,1和10也会被打印出来。
for number in 1...10 {
print("The number is \(number)")
}
FizzBuzz是一个简单的编程练习,我们可以使用forfor-in 循环。提示的内容是这样的
写一个程序,打印从1到n的数字,3的倍数打印 "Fizz "而不是数字,5的倍数打印 "Buzz",对于3和5的倍数,打印 "FizzBuzz "而不是数字。
我们使用闭合范围操作符在数字1到n之间循环,创建一个ClosedRange<Int> 常数。然后,我们再次循环浏览mapping 中的元组,检查元组中的每个元素。如果这个数字是3的倍数,我们就把Fizz 加到string 。
当我们检查mapping 中的每个元素时,如果它也是5的倍数,我们将Buzz 附加到字符串中,结果是FizzBuzz 。
// MARK: - EXAMPLE
func fizzBuzz(for lastNumber: Int) {
var result = [String]()
let mapping = [(number: 3, value: "Fizz"), (number: 5, value: "Buzz")]
for number in 1...lastNumber {
var string = ""
for tuple in mapping {
if number % tuple.number == 0 {
string += tuple.value
}
}
if string == "" {
string += "\(number)"
}
print(result)
}
return result
}
// MARK: - USAGE
fizzBuzz(for: 10)
// MARK: - OUTPUT
["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz"]
使用半开范围运算符
半开范围运算符创建一个不包括最后一个元素的范围。使用这个运算符的一个基本例子是访问一个数组的索引。
for index in 0..<array.count {
// Access the index
}
使用stride
对于你想在一个循环中跳过某个特定数字的元素的情况,你可以使用stride 。我们也可以用它来在一个循环中往回走,从最后一个元素开始,走到第一个元素。
回到刚才的例子,我们用随机值创建了一个大小为10×10的二维矩阵,我们想打印第一行的每一个备用元素。
// MARK: - EXAMPLE
func printFirstRow(for board: [[Int]]) {
for rowIndex in stride(from: board.count - 1, through: 0, by: -2) {
print(board\[rowIndex\][0])
}
}
// MARK: - USAGE
printFirstRow(for: board)
// MARK: - OUTPUT
7
4
4
4
8
现在,我们想打印第一列中的每一个备用元素,但顺序要相反。
// MARK: - EXAMPLE
func printFirstColumn(for board: [[Int]]) {
for rowIndex in stride(from: board.count - 1, through: 0, by: -2) {
print(board\[rowIndex\][0])
}
}
// MARK: - USAGE
printFirstColumn(for: board)
// MARK: - OUTPUT
8
6
0
6
5
字典
我们也可以使用for-in 循环来遍历一个Dictionary ,尽管结果是无序的。语法与数组类似,每个元素都有其键和值。
// MARK: - EXAMPLE
func printDictionary(for numbers: [Int: Int]) {
for number in numbers {
// number is a Dictionary<Int, Int>.Element
print("The value for key \(number.key) is \(number.value)")
}
}
// MARK: - USAGE
let numbers: [Int: Int] = [1: 2, 2: 3, 3: 4]
printDictionary(for: numbers)
// MARK: - OUTPUT
The value for key 1 is 2
The value for key 2 is 3
The value for key 3 is 4
我们也可以明确地使用我们自己的关键字来代替。
// MARK: - EXAMPLE
func printStockPrices(for stocks: [String: Int]) {
for (name, price) in stocks {
print("\(name) is currently valued at $\(price).")
}
}
// MARK: - USAGE
let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]
printStockPrices(for: stocks)
// MARK: - OUTPUT
Banana is currently valued at $125.
Ramalon is currently valued at $3200.
TapeBook is currently valued at $320.
我们也可以在字典中使用where 。
// MARK: - EXAMPLE
func printStockPrices(for stocks: [String: Int]) {
for (name, price) in stocks where name == "Banana" {
print("\(name) is currently valued at $\(price).")
}
}
// MARK: - USAGE
let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]
printStockPrices(for: stocks)
// MARK: - OUTPUT
Banana is currently valued at $125.
如果你想要这个字典中的最高价格,你可以用sorted(by:) 对字典进行排序。
// MARK: - EXAMPLE
func printStockPrices(for stocks: [String: Int]) {
for (name, price) in stocks.sorted(by: { $0.value > $1.value }) {
print("\(name) is currently valued at $\(price).")
}
}
// MARK: - USAGE
let stocks: [String: Int] = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]
printStockPrices(for: stocks)
// MARK: - OUTPUT
Ramalon is currently valued at $3200.
TapeBook is currently valued at $320.
Banana is currently valued at $125.
使用KeyValuePairs
如前所述,Dictionary 并没有定义的排序。如果你想要有序的键值对,你可以使用KeyValuePairs 。这在你愿意为线性时间牺牲快速、恒定的查找时间的情况下很有用。
// MARK: - EXAMPLE
func printStockPrices(for stocks: KeyValuePairs<String, Int>) {
for (name, price) in stocks {
print("\(name) is currently valued at $\(price).")
}
}
// MARK: - USAGE
let stocks: KeyValuePairs = ["Banana": 125, "TapeBook": 320, "Ramalon": 3200]
printStockPrices(for: stocks)
// MARK: - OUTPUT
Banana is currently valued at $125.
TapeBook is currently valued at $320.
Ramalon is currently valued at $3200.
枚举
你甚至可以在Swift中通过符合一个特定的协议来迭代一个枚举,该协议名为CaseIterable 。这种类型提供了一个其所有值的集合。在我们的例子中,它提供了Enum 中的所有情况。为了访问它们,我们使用allCases 属性。
通过另一个例子,我们正在研究一个超休闲游戏。我们需要在主屏幕上设置不同的游戏模式。我们将创建一个enum ,并对其进行迭代以访问模式名称和图像名称。
enum GameModes: String {
case arcade
case challenge
case casual
case timed
}
extension GameModes {
var name: String {
self.rawValue.capitalized
}
var image: String {
switch self {
case .arcade: return "🕹"
case .casual: return "🎮"
case .challenge: return "🎖"
case .timed: return "⏳"
}
}
}
extension GameModes: CaseIterable {}
// Usage
for mode in GameModes.allCases {
let gameOptionsView = GameOptionsView()
gameOptionsStackView.addArrangedSubview(gameOptionsView)
gameOptionsView.set(name: mode.name, image: mode.image)
}
结论
循环是帮助你更好地掌握Swift的基本知识。在这篇文章中,我们通过不同的例子和用例对for-in 循环进行了概述。
for-in循环在Swift中的应用教程》一文首次出现在LogRocket博客上。