使用函数在Swift中查找两个序列中的公共元素

时间:2014-06-04 00:47:14

标签: swift

我正在尝试完成Apple新书“The Swift Programming Language”第46页的练习。它给出了以下代码:

func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
anyCommonElements([1, 2, 3], [3])

练习是更改函数,以便返回两个序列都具有的所有元素。为此,我尝试使用以下代码:

func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element:     Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> T.GeneratorType[] {
    var toReturn = T.GeneratorType[]()
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                toReturn.append(lhsItem)
            }
        }
    }
    return toReturn
}
anyCommonElements([1, 2, 3], [3])

但在第2行,我收到错误:无法找到成员'下标'

出现此错误的原因是什么?此问题的最佳解决方案是什么?

8 个答案:

答案 0 :(得分:27)

通过使返回值为T.GeneratorType.Element数组,我能够使它工作。

func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Array<T.Generator.Element> {
    var toReturn = Array<T.Generator.Element>()
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                toReturn.append(lhsItem)
            }
        }
    }
    return toReturn
}
anyCommonElements([1, 2, 3], [3])

答案 1 :(得分:9)

从Swift 3开始,生成器协议重命名为 Iterator 协议:(link to github proposal)

因此,需要编写函数:

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 common: [T.Iterator.Element] = []

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

答案 2 :(得分:8)

我在上述两个解决方案中遇到了编译错误,我正在Xcode 6.01游乐场的iBook上运行指南。我有关于我在这里找到的数组声明的一致编译器投诉,所以我假设海报可能正在使用早期版本的swift。如果我错了,那就知道了。

对于数组声明,我发现

    var thingList : [ThingType] = []

一直工作,所以我倾向于这样做,放弃

    var thing[],thing[]()  // gave compiler errors/warnings

我的环境永远无法解决名为 T.GeneratorType [.Element]

的事情

我为这个实验找到的解决方案是


func anyCommonElements <T, U
                    where
                    T: SequenceType, U: SequenceType,
                    T.Generator.Element: Equatable,
                    T.Generator.Element == U.Generator.Element>
                    (lhs: T, rhs: U)
-> [T.Generator.Element]
{
    var  returnValue: [T.Generator.Element] = []
    for lhsItem in lhs {
        for rhsItem in rhs {
           if lhsItem == rhsItem {
              returnValue.append(lhsItem)
           }
        }
    }
    return returnValue
}

let commonNumberList = anyCommonElements([1, 2, 3,4,5,6,7,8], [2,3,9,14,8,21])
println("common Numbers = \(commonNumberList)")

let commonStringList = anyCommonElements(["a","b","c"],["d","e","f","c","b"])
println("common Strings = \(commonStringList)")

教程文本确实没有为我准备好实际解决实验而没有大量额外的阅读。感谢大家在这里贡献他们的解决方案,它确实帮助我对Swift进行了很好的介绍。

答案 3 :(得分:1)

问题是将返回值定义为数组,以便可以向其添加元素。

func anyCommonElements<T, U where T:SequenceType, U:SequenceType, 
T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element >(lhs: T, rhs: U) -> Array <T.Generator.Element> {
   var result = Array <T.Generator.Element>();
   for lhsItem in lhs {
       for rhsItem in rhs {
          if lhsItem == rhsItem {
            result.append(lhsItem);
          }
       }
  }
  return result;
}

print(anyCommonElements([1,3,7,9,6,8],rhs:[4,6,8]));

答案 4 :(得分:1)

在最新版本的Swift(5.1)中,以下代码有效:

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Array<T.Element>
    where T.Element: Equatable, T.Element == U.Element
{
    var templist = Array<T.Element>()
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                templist.append(lhsItem)
            }
        }
    }
   return templist
}
anyCommonElements([1, 2, 3], [3])

答案 5 :(得分:0)

虽然这个问题已经得到解答,而最初的问题是关于使用泛型数组,但有一种方法可以使用Set并改进stackoverflow知识库,我仍然希望发布它。

swift类Set包含以下四种方法:

Set.union(sequence:)
Set.subtract(sequence:)
Set.intersect(sequence:)
Set.exclusiveOr(sequence:)

在此处记录:https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_Set_Structure/index.html

您可以将两个数组转换为集合并使用以下方法:

let array1 = [1, 2, 3]
let array2 = [3, 4]

let set1 = Set<Int>(array1)
let set2 = Set<Int>(array2)

let union = set1.union(set2)                // [2, 3, 1, 4]
let subtract = set1.subtract(set2)          // [2, 1]
let intersect = set1.intersect(set2)        // [3]
let exclusiveOr = set1.exclusiveOr(set2)    // [2, 4, 1]

编辑1:

与评论中提到的Martin R一样,Set<T>的类型必须继承协议Hashable,这比Equatable稍微限制一些。 并且元素的顺序也不会被保留,因此请考虑有序元素的相关性!

答案 6 :(得分:0)

接受的答案不再适用于最新版本的Swift。这是一个与3.0.1版兼容的更新,它们简化了如何制作通用数组。

func showCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
        var result:[T.Iterator.Element] = []
        for lhsItem in lhs {
            for rhsItem in rhs {
                if lhsItem == rhsItem {
                    result.append(lhsItem)
                }
            }
        }
        return result
}

您可以使用以下命令测试代码:

showCommonElements([1, 2, 3, 4, 5], [4, 7, 3])
showCommonElements(["apple", "banana", "orange", "peach"], ["orange", "pear", "apple"])

答案 7 :(得分:0)

在Swift 3中解决这个问题的最简单方法是使用过滤函数,如下所示:

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> [T.Iterator.Element]
    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
        return lhs.filter { rhs.contains($0) }
}