Ken 的回答很好,但是 Collection 可以有一个不是 Int 的索引。如果您尝试使用返回的索引,您将无法使用它们进行下标。
你想要[Self.Index],而不是[Int]。所以你想要更通用的zip,而不是enumerated:
extension Collection where Self.Element: Equatable {
func indicesOfElements(equalTo element: Self.Element) -> [Self.Index] {
return zip(self.indices, self) // Zip together the index and element
.filter { $0.1 == element } // Find the ones that match
.map { $0.0 } // Return the elements
}
}
[1,2,3,4,1].indicesOfElements(equalTo: 1) // => [0,4]
也就是说,更简单的方法是:
extension Collection where Self.Element: Equatable {
func indicesOfElements(equalTo element: Self.Element) -> [Self.Index] {
return indices.filter { self[$0] == element }
}
}
要查看下标问题,请考虑以下问题:
let s = "abcabc"
let i = s.indicesOfElements(equalTo: "a").first!
s[i] // "a"
let j = findAll(s, "a").first!
s[j] // error: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion
虽然协议扩展是在 Swift 中执行此操作的首选方式,但它可以直接转换为以下通用函数语法:
func indicesOfElements<C: Collection>(in collection: C, equalTo element: C.Element) -> [C.Index]
where C.Element: Equatable {
return collection.indices.filter { collection[$0] == element }
}
下面的风格也是等价的,学习曲线稍浅(filter不用。
func simplerIndicesOfElements<C: Collection>(in collection: C, equalTo element: C.Element) -> [C.Index]
where C.Element: Equatable {
var results: [C.Index] = []
for index in collection.indices {
if collection[index] == element {
results.append(index)
}
}
return results
}
我相信即使是相当新的 Swift 开发人员也应该学会阅读简单的 filter 和 map 表达式(尽管这些表达式应该保持简单,即使是专家!)但是学习简单的 for 迭代绝对没有错首先。
如果您刚刚开始,请注意此处的命名样式。您的初始示例包括两个未命名的参数。在许多(大多数)情况下,这是糟糕的 Swift。在 Swift 中,我们通常会尝试命名我们的参数,以便它们在英文中读起来相当自然。在findAll(xs, x) 中,不清楚参数是什么或返回值是什么。在indicesOfElements(in: xs, equalTo: x) 中,所有信息都可以在调用站点获得。
在 Swift 3 中,这需要比您预期的更多的语法:
func indicesOfElements<C: Collection>(in collection: C, equalTo element: C.Iterator.Element) -> [C.Index]
where C.Iterator.Element: Equatable, C.Indices.Iterator.Element == C.Index {
// ... Same body ...
}
在 Swift 3 中,关联类型不能有额外的约束。这似乎是一件小事,但它是巨大的。这意味着无法说 Collection.Indices 实际上包含 Collection.Index。并且无法创建承诺与 Collection.Iterator.Element (通过迭代集合返回的类型)相同的 Collection.Element(通过下标集合返回的类型)。这导致很多C.Iterator... 和where 子句看起来很明显,例如C.Indices.Iterator.Element == C.Index。
如果您即将开始您的 Swift 之旅,我可能会完全跳过泛型,并以 [Int] 的形式来写。 Swift 程序员(新的和“旧的”)一个非常常见的错误是在他们需要之前跳到泛型编程。但是,如果您正处于处理泛型的阶段,这就是您经常必须在 Swift 3 中编写它们的方式。(Swift 4 是泛型编程的一个显着改进。)