【问题标题】:Unable to use contains within a Swift Array extension无法在 Swift 数组扩展中使用包含
【发布时间】:2014-06-06 21:41:52
【问题描述】:

我正在尝试编写一个提供“不同”方法的简单数组扩展。这是我目前所拥有的:

extension Array {
  func distinct() -> T[] {
    var rtn = T[]()

    for x in self {
      var containsItem = contains(rtn, x)
      if !containsItem {
        rtn.append(x)
      }
    }
    return rtn
  }
 }

问题是'contains'语句失败如下:

找不到接受所提供参数的“包含”的重载

我很确定类型约束是正确的。有什么想法吗?

【问题讨论】:

    标签: swift


    【解决方案1】:

    斯威夫特 1.x

    array 中的元素不必是Equatable,即它们不能与== 相提并论。

    这意味着您不能为所有个可能的数组编写该函数。而且 Swift 不允许你只扩展数组的一个子集。

    这意味着你应该把它写成一个单独的函数(这可能就是为什么contains 也不是一个方法)。

    let array = ["a", "b", "c", "a"]
    
    func distinct<T: Equatable>(array: [T]) -> [T] {
        var rtn = [T]()
    
        for x in array {
            var containsItem = contains(rtn, x)
            if !containsItem {
                rtn.append(x)
            }
        }
        return rtn
    }
    
    distinct(array) // ["a", "b", "c"]
    

    Swift 2/Xcode 7(测试版)更新

    Swift 2 支持将扩展限制为协议实现的子集,因此现在允许以下操作:

    let array = ["a", "b", "c", "a"]
    
    extension SequenceType where Generator.Element: Comparable {
        func distinct() -> [Generator.Element] {
            var rtn: [Generator.Element] = []
    
            for x in self {
                if !rtn.contains(x) {
                    rtn.append(x)
                }
            }
            return rtn
        }
    }
    
    array.distinct() // ["a", "b", "c"]
    

    注意苹果如何使用相同的语法添加SequenceType.contains

    【讨论】:

    • 如果数组的类型不符合Equatable,您能否引发异常或以某种方式返回 nil?这将允许扩展 Array,并优雅地处理不受支持的类型。
    • @AlexWayne 你可以通过对新的本地模板类型进行可选转换来让它静默失败(我不会说它是优雅的)。您可以在我的答案here 上看到一个示例
    【解决方案2】:

    终于知道怎么弄了:

    extension Array {
        func contains<T : Equatable>(obj: T) -> Bool {
            return self.filter({$0 as? T == obj}).count > 0
        }
    
        func distinct<T : Equatable>(_: T) -> T[] {
            var rtn = T[]()
    
            for x in self {
                if !rtn.contains(x as T) {
                    rtn += x as T
                }
            }
    
            return rtn
        }
    }
    

    以及使用/测试:

    let a = [ 0, 1, 2, 3, 4, 5, 6, 1, 2, 3 ]
    
    a.contains(0)
    a.contains(99)
    
    a.distinct(0)
    

    不幸的是,我无法找到一种方法来做到这一点,而不必指定随后被忽略的参数。它存在的唯一原因是调用 distinct 的正确形式。对于distinct,这种方法的主要优势似乎在于它不会将distinct 之类的常用术语转储到全局命名空间中。对于 contains 情况,它看起来确实更自然。

    【讨论】:

    • 是的。不幸的是,根据设计,这允许您调用诸如a.distinct("a") 之类的不合逻辑的东西,而对于该函数,编译器可能会给您一个很好的错误。情况总是如此,即使有办法摆脱参数,因为两个 Ts 不会强制匹配。
    • @nschum 在我的测试中,我无法输入错误的类型。
    • 真的吗?对我来说 [0, 1].distinct("a") 在运行时会导致 EXC_BAD_ACCESS。
    • @nschum 嗯......我认为我至少尝试过,但现在我似乎看到了同样的事情。不知道:)
    • 如果它在运行时崩溃,那么类型安全的意义何在 :)
    【解决方案3】:

    另一种解决方案是使用 find(Array:[T], obj:T) 函数。它会返回一个可选的 Int,所以你可以做的是

    if let foundResult = find(arr, obj) as Int
    {
         //obj is contained in arr
    } else
    {
         //obj is not contained in arr.
    }
    

    【讨论】:

      【解决方案4】:

      从 Swift 2 开始,这可以通过协议扩展方法来实现, 例如在所有符合SequenceType 的类型上,其中序列 元素符合Equatable:

      extension SequenceType where Generator.Element : Equatable {
      
          func distinct() -> [Generator.Element] {
              var rtn : [Generator.Element] = []
      
              for elem in self {
                  if !rtn.contains(elem) {
                      rtn.append(elem)
                  }
              }
      
              return rtn
          }
      }
      

      例子:

      let items = [1, 2, 3, 2, 3, 4]
      let unique = items.distinct()
      print(unique) // [1, 2, 3, 4]
      

      如果元素被进一步限制为Hashable,那么你 可以利用Set 类型:

      extension SequenceType where Generator.Element : Hashable {
      
          func distinct() -> [Generator.Element] {
              return Array(Set(self))
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多