Swift 中的泛型

Fra*_*kie 5 generics swift

我正在学习 Swift 中的泛型。对我来说,这个话题很难理解。在我正在读的书中,关于泛型有两个挑战:

第一个挑战:它要求编写一个函数findAll(_:_:),该函数接受符合 Equatable 协议的任何类型 T 的数组和单个元素(也是 T 类型)。findAll(_:_:)应该返回一个整数数组,对应于数组中找到该元素的每个位置。例如,findAll([5,3,7,3,9], 3]应该返回[1,3].

第二个挑战:修改findAll(_:_:)为接受 Collection 而不是数组,并给出提示“您需要将返回类型从 [Int] 更改为 Collection 协议关联类型的数组”

这就是我为第一个挑战所做的

func findAll<T:Equatable> (_ first: [T], _ second: T) -> [Int] {
var array = [Int]()

for i in 0..<first.count {
    if first[i] == second {
        array.append(i)
        }
    }   
return array
}
Run Code Online (Sandbox Code Playgroud)

对于第二个挑战,我正在考虑的是一个可以传递集合(可以是数组、字典或集合)的通用函数。但是对于Set类型,由于它没有定义的顺序,如何找到Set中项目的位置?

谢谢。

Mar*_*n R 2

集合的下标方法定义为

public subscript(position: Self.Index) -> Self.Iterator.Element { get }
Run Code Online (Sandbox Code Playgroud)

这意味着你的函数应该作为参数

  • 一个集合C,以及
  • 关联类型的值C.Iterator.Element

并返回一个数组C.Index。此外,元素类型应该是Equatable

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
    where C.Iterator.Element: Equatable
{ ... }
Run Code Online (Sandbox Code Playgroud)

与数组的解决方案类似,可以循环遍历集合的索引:

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
where C.Iterator.Element: Equatable
{
    var result: [C.Index] = []

    var idx = collection.startIndex
    while idx != collection.endIndex {
        if collection[idx] == element {
            result.append(idx)
        }
        collection.formIndex(after: &idx)
    }

    return result
}
Run Code Online (Sandbox Code Playgroud)

人们会期望像这样的东西

for idx in collection.startIndex ..< collection.endIndex
// or
for idx in collection.indices
Run Code Online (Sandbox Code Playgroud)

可行,但是(在 Swift 3 中)这需要对关联类型进行额外的约束Indices

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
    where C.Iterator.Element: Equatable, C.Indices.Iterator.Element == C.Index
{

    var result: [C.Index] = []

    for idx in collection.indices {
        if collection[idx] == element {
            result.append(idx)
        }
    }

    return result
}
Run Code Online (Sandbox Code Playgroud)

这在 Swift 4 中不再是必需的,例如,请参阅 Unable to useindexs.contains() in a Collection extension in Swift 3以获得更好的解释。

现在可以使用以下方法来简化filter

func findAll<C: Collection> (_ collection: C, _ element: C.Iterator.Element) -> [C.Index]
    where C.Iterator.Element: Equatable, C.Indices.Iterator.Element == C.Index
{
    return collection.indices.filter { collection[$0] == element }
}
Run Code Online (Sandbox Code Playgroud)

示例( 的集合Character):

let chars = "abcdabcdabcd".characters
let indices = findAll(chars, "c")
for idx in indices {
    print(chars[idx])
}
Run Code Online (Sandbox Code Playgroud)

Set也是a Collection,它有一个关联的Index 类型和一个subscript方法。例子:

let set = Set([1, 2, 3, 4, 5, 6, 7, 8, 9])
let indices = findAll(set, 3)
for idx in indices {
    print(set[idx])
}
Run Code Online (Sandbox Code Playgroud)

最后,您可能希望将函数定义为类型 上的方法Collection

extension Collection where Iterator.Element: Equatable, Indices.Iterator.Element == Index {
    func allIndices(of element: Iterator.Element) -> [Index] {
        return indices.filter { self[$0] == element }
    }
}

// Example:
let indices = [1, 2, 3, 1, 2, 3].allIndices(of: 3)
Run Code Online (Sandbox Code Playgroud)