我正在尝试编写一个接受泛型Collection和单个元素的函数,然后将该元素的索引或索引作为数组返回。但是,我在return语句中收到错误。
func findAll<T: Collection, U: Equatable>(_ items: T, _ find: U) -> [U] where T.Iterator.Element == U {
var found = [Int]()
for (index, item) in items.enumerated() {
if item == find {
found.append(index)
}
}
return found
}
答案 0 :(得分:3)
Ken的答案很好,但Collection可以有一个除Int之外的索引。如果你试图使用你得到的索引,那么很多人就无法用它们下标。
而不是[Int]
,您需要[Self.Index]
。因此,您需要更通用的enumerated
:
zip
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.Element(由订阅集合返回的类型),它被承诺与Collection.Iterator.Element相同(迭代通过集合返回的类型)。这会导致很多C.Iterator...
和where
条款显得像C.Indices.Iterator.Element == C.Index
一样明显。
如果你接近Swift旅程的开始,我可能会完全跳过泛型,并以[Int]
的形式写出来。 Swift程序员(新的和“旧的”)的一个常见错误是在他们需要它之前跳转到泛型编程。但是如果你正处于你正在处理泛型的阶段,那么你经常需要在Swift 3中编写它们。(Swift 4是对泛型编程的一个显着改进。)
答案 1 :(得分:1)
既然你想返回找到的元素的索引,你应该返回一个Ints数组而不是[U]。这是更新后的代码:
func findAll<T: Collection, U: Equatable>(_ items: T, _ find: U)
-> [Int] where T.Iterator.Element == U {
var found = [Int]()
for (index, item) in items.enumerated() {
if item == find {
found.append(index)
}
}
return found
}
这是一个测试用例:
let numbers = [1,0,1,0,0,1,1]
let indicesOfOnes = findAll(numbers, 1)
print(indicesOfOnes)
将打印:
[0, 2, 5, 6]