Swift循环

67 阅读5分钟

Swift循环

hudson 译 原文

Swift提供了许多不同的内置方法来遍及集合(如数组、集合和字典)——其中最基本的是for循环,它允许我们为给定集合中发现的每个元素运行一段代码。例如,在这里,我们循环一个names数组,然后通过将每个名称打印到控制台来输出每个名称:

let names = [“John”, “Emma”, “Robert”, “Julia”]

for name in names {
    print(name)
}

另一种方法是在name数组上调用forEach方法,效果完成相同。后者可以为每个找到的元素运行的一个闭包:

names.forEach { name in
    print(name)
}

然而,for循环和forEach之间的一个关键区别是,后者不能在满足给定条件后中断或者停止循环。例如,当再次使用for循环时,一旦遇到Robert这个名字,我们可以决定停止循环:

let names = [“John”, “Emma”, “Robert”, “Julia”]

for name in names {
    if name ==Robert” {
    break
}

    print(name) // Will only print “John” and “Emma”
}

在Swift中还有许多其他用于for循环的方法。例如,如果想访问目前作为循环一部分的正在处理的index,那么可以选择将循环基于从零到集合中元素数量的范围。然后,使用数组类型的下标功能来检索该索引的当前元素——像这样:

for index in 0..<names.count {
    print(index, names[index])
}

也可以直接使用数组的indices属性循环,而不是手动构造一个范围,二者功能完全相同:

for index in names.indices {
    print(index, names[index])
}

还有一种方法是使用enumrated方法将数组转换一个元组的序列,元组将每个索引与其相关元素配对:

for (index, name) in names.enumerated() {
    print(index, name)
}

请注意,enumrated方法始终使用基于Int的偏移量,在Array的情况下,这是一个完美的匹配,因为该集合也使用Int值作为其索引。

接下来,让我们看看while循环,只要给定的布尔条件仍然为真,它提供了一种重复运行代码块的方法。例如,以下是如何使用while循环来一直将names数组中的每个名称附加到一个字符串中,只要该字符串包含少于8个字符:

let names = [“John”, “Emma”, “Robert”, “Julia”]
var index = 0
var string = “”

while string.count < 8 {
    string.append(names[index])
    index += 1
}

print(string) // “JohnEmma”

构建while循环的另一种方法是使用单独的repeat块(在Swift中可能不像其他语言那样常用),只要while条件计算为true,该重复块也会重复运行:

let names = [“John”, “Emma”, “Robert”, “Julia”]
var index = 0
var string = “”

repeat {
    string.append(names[index])
    index += 1
} while string.count < 8

print(string) // “JohnEmma”

两种while循环之间的关键区别是,重复块将始终至少运行一次,即使附加的while条件最初评估为false

然而,在使用while循环时,需要记住的一件重要事情是,要确保每个循环在适当的时间结束——要么手动使用break(就像我们之前使用for循环时所做的那样),要么确保在迭代终止后满足循环的布尔条件。

例如,在构建基于名称的string字符串值时,可能希望确保当前索引不会超出names数组的边界——否则应用程序在下标进入该数组时会崩溃。一种方法是将第二个布尔条件附加到while语句中——像这样:

while string.count < 8, index < names.count {
    string.append(names[index])
    index += 1
}

另一种方法是在循环体本身内执行上述索引检查,例如使用guard语句,该语句将在其else子句中中断循环:

while string.count < 8 {
    guard index < names.count else {
    break
}

    string.append(names[index])
    index += 1
}

在处理循环时,通常有许多不同的方法来建模相同的逻辑,最好先进行简单评估,以找出哪种方法在哪种情况下效果最好。例如,我们实际上可以选择使用for循环来实现上述迭代——因为可以使用where关键字将基于string.count的条件附加到这样的循环中:

let names = [“John”, “Emma”, “Robert”, “Julia”]
var string = “”

for name in names where string.count < 8 {
    string.append(name)
}

print(string) // “JohnEmma”

这并不意味着上述for循环版本客观上优于while循环版本。选择使用哪种类型的循环通常是品味和代码结构的问题,尽管我个人认为,当循环基于实际的元素集合时,使用for循环通常是最简单的方法。

最后,让我们把注意力转向字典。字典也可以使用用于循环数组和范围的完全相同的工具进行遍历。然而,在字典上进行遍历时,不只是访问单个元素,还可以访问(keyvaule)元组对。例如,以下是我们如何通过包含我们名称数据集基于类别版本的字典进行遍默:

let namesByCategory = [
    “friends”: [“John”, “Emma”],
    “family”: [“Robert”, “Julia”]
]

for (category, names) in namesByCategory {
    print(category, names)
}

有时可能不需要同时访问字典的键和值,可以使用_符号来忽略相应元组成员。以下代码忽略字典的值,只在循环中处理其键(或类别):

for (category, _) in namesByCategory {
    print(category)
}

然而,虽然上述是完全有效的Swift代码,但实际上字典有两个专门构建的API,可以仅对keys键属性或仅对值values 属性进行循环 :

for category in namesByCategory.keys {
    print(category)
}

for names in namesByCategory.values {
    print(names)
}

字典(dictionary)、集合(sets),还有一些其他collections不保证元素顺序,重要的是要记住,其上的的循环也不会以可预测的顺序执行。因此,例如,如果想确保上述category迭代始终以相同的顺序进行,那么可以首先将字典按键排序为数组来实现这一点——像这样:

for category in namesByCategory.keys.sorted() {
    print(category)
}

现在类别将始终按字母顺序遍历。当然,只有当元素的顺序很重要时,才需要执行上述类型的排序操作。

我希望您发现这篇基础知识文章有用,并且您至少学会了一种在Swift中构建循环的新方法。如果您有任何问题、评论或反馈(即使是积极的!),请随时通过Twitter电子邮件联系。

感谢您的阅读!