问题
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:
- 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 thancommonElements(a, b). - The nested for-loops in your approach lead to
O(N^2)time complexity. This approach isO(N), but has the additional constraint thatElementisHashable. A secondary definition can be made that works for non-hashable Elements. It has worse space complexity than yours. Yours isO(1), whereas this is hasO(M)space complexity, whereMis the size of the second list. - 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