何时在 Swift 中使用序列、迭代器、集合等协议
When to use Protocols like Sequence, Iterator, Collection in Swift
我正在学习 swift 教程,我遇到了实现协议的示例,例如序列,有时是集合,有时是迭代器
我查看了何时真正使用此协议,但从未找到。所有其他材料都说明了如何使用它,但没有说明何时使用它。
谁能告诉我在哪里可以学到这个。任何快速提示都会很有帮助。
谢谢
为特定类型(例如 Array
)编写例程通常是最简单的,但是为 Sequence
协议编写它们提供了更大的灵活性。我在为集合编写扩展时最常遇到这种情况,根据定义,集合可以在其他地方重用,并且灵活性最有价值。
例如,考虑这个 Array
对值求和或打印值的扩展:
extension Array where Element == Int {
func sum() -> Int {
var sum = 0
for element in self {
sum += element
}
return sum
}
func printValues() {
print("printing values")
for element in self {
print(element)
}
}
}
如果你真的有一个数组,这显然有效,例如
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let total = array.sum() // fine
array.printValues() // fine
但是如果你有一个子范围,那将不起作用,例如:
let total = array[0...3].sum() // this won’t work
array[0...3].printValues() // this won’t work
或者如果你有一套:
let set: Set = [1, 2, 3]
let total = set.sum() // this won’t work
您必须将此子数组或 Set
转换为数组才能工作。但是,与其将其定义为 Array
扩展,不如将扩展定义为 Sequence
:
extension Collection where Element: AdditiveArithmetic {
func sum() -> Element {
var sum: Element = .zero
for element in self {
sum += element
}
return sum
}
}
extension Sequence where Element: CustomStringConvertible {
func printValues() {
print("printing values")
for element in self {
print(element)
}
}
}
看起来几乎一样,但这些演绎版适用于适当的类型,例如:
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(array[0..<3].sum()) // works for array slices
array[0..<3].printValues()
(200..<300).printValues() // or ranges
在上面,我在适当的地方使用了Sequence
/Collection
。我还更改了 sum
以便它可以用于符合 AdditiveArithmetic
的任何类型(无符号整数、32 位整数、浮点类型等),而不是仅限于整数。同样,对于 printValues
,它现在支持具有符合 CustomStringConvertible
值的任何序列(即任何可以打印的序列)。在这两种情况下,我们的想法是使扩展尽可能灵活,以便它适用于任何符合所讨论方法逻辑的类型。
let total = [1.25, 2.3, 3.3].sum()
["moe", "larry", "curly"].printValues()
我可能更喜欢协议而不是特定类型的几个例子:
当编写接受字符串输入的例程时,我可能会把它写成接受 StringProtocol
,这样例程就可以同时接受字符串和子字符串(例如 string[start..<end]
)。
我在处理范围时采用了类似的思维过程,理想情况下算法应该在封闭范围、开放范围、部分范围等上工作。我将使用 RangeExpression
其中我需要一些灵活性,当我正在寻找快速简单的东西时,我会使用 Range
。
话虽如此,通常为特定类型编写方法比为协议编写方法更简单,因此应该在使用协议提供的灵活性与为特定类型编写方法的便利性之间取得平衡。就个人而言,如果它是一些不会在其他地方使用的私有方法,我会继续写简单的 type-specific 演绎版,但如果它是一些 public/internal 实用方法,可能会享受一些 re-use,我将使用协议再现以获得最大的灵活性。
最重要的是,在编写方法时,问问自己是否重要的是该方法仅适用于 Array
类型,或者是否存在相同例程可能有用的其他序列。
就我何时为自己的类型使用 Sequence
或迭代器而言,这种情况要少得多。我将在编写真正的“序列”的地方执行此操作,其中下一个值是从前面的值生成的,例如一个斐波那契数列或这个 count-and-say sequence。您最终可以在调用点与序列进行一些非常自然的交互,而不是将结果 shoe-horning 放入数组中。但是我们不会像 real-world 应用程序中的简单数组那样频繁地遇到这种模式。
我正在学习 swift 教程,我遇到了实现协议的示例,例如序列,有时是集合,有时是迭代器
我查看了何时真正使用此协议,但从未找到。所有其他材料都说明了如何使用它,但没有说明何时使用它。
谁能告诉我在哪里可以学到这个。任何快速提示都会很有帮助。
谢谢
为特定类型(例如 Array
)编写例程通常是最简单的,但是为 Sequence
协议编写它们提供了更大的灵活性。我在为集合编写扩展时最常遇到这种情况,根据定义,集合可以在其他地方重用,并且灵活性最有价值。
例如,考虑这个 Array
对值求和或打印值的扩展:
extension Array where Element == Int {
func sum() -> Int {
var sum = 0
for element in self {
sum += element
}
return sum
}
func printValues() {
print("printing values")
for element in self {
print(element)
}
}
}
如果你真的有一个数组,这显然有效,例如
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let total = array.sum() // fine
array.printValues() // fine
但是如果你有一个子范围,那将不起作用,例如:
let total = array[0...3].sum() // this won’t work
array[0...3].printValues() // this won’t work
或者如果你有一套:
let set: Set = [1, 2, 3]
let total = set.sum() // this won’t work
您必须将此子数组或 Set
转换为数组才能工作。但是,与其将其定义为 Array
扩展,不如将扩展定义为 Sequence
:
extension Collection where Element: AdditiveArithmetic {
func sum() -> Element {
var sum: Element = .zero
for element in self {
sum += element
}
return sum
}
}
extension Sequence where Element: CustomStringConvertible {
func printValues() {
print("printing values")
for element in self {
print(element)
}
}
}
看起来几乎一样,但这些演绎版适用于适当的类型,例如:
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(array[0..<3].sum()) // works for array slices
array[0..<3].printValues()
(200..<300).printValues() // or ranges
在上面,我在适当的地方使用了Sequence
/Collection
。我还更改了 sum
以便它可以用于符合 AdditiveArithmetic
的任何类型(无符号整数、32 位整数、浮点类型等),而不是仅限于整数。同样,对于 printValues
,它现在支持具有符合 CustomStringConvertible
值的任何序列(即任何可以打印的序列)。在这两种情况下,我们的想法是使扩展尽可能灵活,以便它适用于任何符合所讨论方法逻辑的类型。
let total = [1.25, 2.3, 3.3].sum()
["moe", "larry", "curly"].printValues()
我可能更喜欢协议而不是特定类型的几个例子:
当编写接受字符串输入的例程时,我可能会把它写成接受
StringProtocol
,这样例程就可以同时接受字符串和子字符串(例如string[start..<end]
)。我在处理范围时采用了类似的思维过程,理想情况下算法应该在封闭范围、开放范围、部分范围等上工作。我将使用
RangeExpression
其中我需要一些灵活性,当我正在寻找快速简单的东西时,我会使用Range
。
话虽如此,通常为特定类型编写方法比为协议编写方法更简单,因此应该在使用协议提供的灵活性与为特定类型编写方法的便利性之间取得平衡。就个人而言,如果它是一些不会在其他地方使用的私有方法,我会继续写简单的 type-specific 演绎版,但如果它是一些 public/internal 实用方法,可能会享受一些 re-use,我将使用协议再现以获得最大的灵活性。
最重要的是,在编写方法时,问问自己是否重要的是该方法仅适用于 Array
类型,或者是否存在相同例程可能有用的其他序列。
就我何时为自己的类型使用 Sequence
或迭代器而言,这种情况要少得多。我将在编写真正的“序列”的地方执行此操作,其中下一个值是从前面的值生成的,例如一个斐波那契数列或这个 count-and-say sequence。您最终可以在调用点与序列进行一些非常自然的交互,而不是将结果 shoe-horning 放入数组中。但是我们不会像 real-world 应用程序中的简单数组那样频繁地遇到这种模式。