Swift 3 Generics: How to Find The Common Set of Two Generic Arrays

*爱你&永不变心* 提交于 2019-12-12 05:09:01

问题


I am trying to find the common set of two generic arrays in Swift 3. I got a type compatibility error with the code below. This happens on the line where I am trying to add an element to the commons array: "error: cannot invoke 'append' with an argument list of type '(T.Iterator.Element)' commonSet.append(lhsItem)"

What is the solution to this problem?

func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T]
    where T.Iterator.Element: Equatable,
          T.Iterator.Element == U.Iterator.Element
{
    var commonSet = [T]()

    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                commonSet.append(lhsItem)
            }
        }
    }

    return commonSet
}

commonElements([1,2,3], [3,5])

回答1:


Solution for Hashable types

extension Sequence where Iterator.Element: Hashable {
    func intersect<T: Sequence>(with other: T) -> [Iterator.Element]
    where T.Iterator.Element == Self.Iterator.Element {
        let otherSet = Set(other)
        return self.filter(otherSet.contains)
    }
}

That's how I would write this, which contains a few modifications of your code:

  1. Instead of writing a global function (or worse, a static function that's arbitrarily nested in some unrelated type) with 2 parameters, you can write it as an extension of Sequence. This brings about some nice syntax: a.intersect(with: b), rather than commonElements(a, b).
  2. The nested for-loops in your approach lead to O(N^2) time complexity. This approach is O(N), but has the additional constraint that Element is Hashable. A secondary definition can be made that works for non-hashable Elements. It has worse space complexity than yours. Yours is O(1), whereas this is has O(M) space complexity, where M is the size of the second list.
  3. I changed the appending of an initially-empty Array to the filtering of an initially full Array. It's shorter syntax.

Solution for Comparable types

extension Sequence where Iterator.Element: Comparable {
    func intersect<T: Sequence>(with other: T) -> [Iterator.Element]
    where T.Iterator.Element == Self.Iterator.Element {
        let arrayA = self.sorted()
        let arrayB = other.sorted()
        var indexA = 0, indexB = 0

        var intersection: [Iterator.Element] = []
        while indexA < arrayA.count && indexB < arrayB.count {
            let a = arrayA[indexA]
            let b = arrayB[indexB]

            if      (a < b) { indexA += 1 }
            else if (b < a) { indexB += 1 }
            else /* implied: a == b */ {
                intersection.append(a)
                indexA += 1
                indexB += 1
            }
        }

        return intersection;
    }
}

This solution is slower, with O(N * log_2(N)) complexity, but still much better than O(N^2). It has space complexity O(2N), because it makes duplicated, sorted copies of both sequences.




回答2:


OK, it is my silly mistake. Thanks to @Hamish's comment, I realized that I was trying to build a common set array of type T and trying to return it, instead of [T.Iterator.Element]. So the fixed version is this:

func commonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
    where T.Iterator.Element: Equatable,
          T.Iterator.Element == U.Iterator.Element
{
    var commonSet: [T.Iterator.Element] = []

    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                commonSet.append(lhsItem)
            }
        }
    }

    return commonSet
}

commonElements([1,2,3], [3,5])


来源:https://stackoverflow.com/questions/40207451/swift-3-generics-how-to-find-the-common-set-of-two-generic-arrays

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!