如何在 Swift 中优雅地比较元组?

问题描述 投票:0回答:7

我确实有 2 个不同类型的元组(Double、Double):

let tuple1: (Double, Double) = (1, 2)
let tuple2: (Double, Double) = (3, 4)

我想使用一个简单的 if 语句来比较它们的值。比如:

if (tuple1 == tuple2) {
    // Do stuff
}

这会引发以下错误:

找不到接受提供的“==”重载 争论

我当前的解决方案是这样的函数:

func compareTuples <T: Equatable> (tuple1: (T, T), tuple2: (T, T)) -> Bool {
    return (tuple1.0 == tuple2.0) && (tuple1.1 == tuple2.1)
}

我已经尝试编写一个扩展,但无法使其适用于元组。对于这个问题你有更优雅的解决方案吗?

comparison swift tuples
7个回答
30
投票

更新

正如 Martin R 在评论中所述,现在可以将最多包含六个组件的元组与

==
进行比较。具有不同组件数量或不同组件类型的元组被认为是不同的类型,因此无法比较它们,但我下面描述的简单情况的代码现在已过时。


试试这个:

func == <T:Equatable> (tuple1:(T,T),tuple2:(T,T)) -> Bool
{
   return (tuple1.0 == tuple2.0) && (tuple1.1 == tuple2.1)
}

和你的一模一样,但我叫它

==
。然后是这样的事情:

(1, 1) == (1, 1)

是真的并且

(1, 1) == (1, 2)

都是假的


12
投票

我同意这种行为不是预期的,因为元组可以在不同的语言(例如Python和Haskell)中进行比较,但是根据官方文档

注意

元组对于相关值的临时组很有用。他们不是 适合创建复杂的数据结构。如果你的数据 结构可能会持续超出临时范围,将其建模为 类或结构,而不是元组。有关更多信息,请参阅 类和结构。

因此,这可能在 Swift 中相当于“你拿错了手机”,但根据当前的指导方针,执行此操作的惯用方法是定义一个类或结构体(甚至是一个枚举),并提供一个可比较的实现来提供

==
以及相关运营商。


7
投票

Swift 4 支持元组比较。您将不会再收到错误。

这段代码运行完美

let tuple1 : (Double, Double) = (1,2)
let tuple2 : (Double, Double) = (3,4)

if (tuple1 == tuple2) {
    print("equal")
}
else {
    print("unequal")
}

这里,unequal 被打印在游乐场的控制台中。

苹果文档中提到的元组比较的一个限制是 -

Swift 标准库包含元组比较运算符 元素少于七个的元组。将元组与七个或 更多元素,您必须自己实现比较运算符。


3
投票

Swift 中存在我们所知的元组。元组可以使用标准 C 运算符进行相互比较。元组从左到右相互比较

if (1,"death") < (3,"life") {   
     print("Life is better than death") // this is true
}

Swift 只比较整数值 1 和 3。就是这样,Swift 不比较死亡和生命的字符串。仅当元组的第一个元素相同时才会比较它们。

if (99,"life") < (99,"death") {   
     print("Life is better than death") // this is false
}

在上面的例子中,swift 不会比较 99 的整数值,而是比较生与死。另外:Swift 只能比较最多有 6 个元素的元组。超过6个元素就得自己比较了。


2
投票

与@JeremyP的答案类似,但更通用:

func ==<T1: Equatable, T2: Equatable>(lhs: (T1, T2), rhs: (T1, T2)) -> Bool {
    return lhs.0 == rhs.0 && lhs.1 == rhs.1
}

func ==<T1: Equatable, T2: Equatable, T3: Equatable>(lhs: (T1, T2, T3), rhs: (T1, T2, T3)) -> Bool {
    return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}

func ==<T1: Equatable, T2: Equatable, T3: Equatable, T4: Equatable>(lhs: (T1, T2, T3, T4), rhs: (T1, T2, T3, T4)) -> Bool {
    return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2 && lhs.3 == rhs.3
}

1
投票

以下方法比较任意数量、任意大小的成员的元组,前提是它们不包含

Array
Dictionary
等集合类型。

import Darwin // or Foundation
/// here we go
func memeq<T>(var lhs: T, var rhs: T) -> Bool {
    return withUnsafePointers(&lhs, &rhs) {
        memcmp($0, $1, UInt(sizeof(T)))
    } == 0
}

let l = (false, 42,  log(exp(1.0)))
let r = (!true, 6*7, exp(log(1.0)))
println(memeq(l, r))   // expectedly true
let l2 = (0, [0])
let r2 = (0, [0])
println(memeq(l2, r2)) // unfortunately false

注释类型已通过泛型进行检查。如果它们不同,由于类型检查,它甚至无法编译。


0
投票

此解决方案基于我之前的答案如何比较“任何”值类型,但包括任何类型的任意数量的项目的元组比较:

fileprivate extension Equatable {
  func isEqual(to: Any) -> Bool {
    self == to as? Self
  }
}

func ==<T>(lhs: T?, rhs: T?) -> Bool where T: Any {
  guard let lhs, let rhs else {
    return lhs == nil && rhs == nil
  }
  
  // Equatable
  if let isEqual = (lhs as? any Equatable)?.isEqual {
    return isEqual(rhs)
  }
  // [Any]
  else if let lhs = lhs as? [Any], let rhs = rhs as? [Any], lhs.count == rhs.count {
    return lhs.elementsEqual(rhs, by: ==)
  }
  // [AnyHashable: Any]
  else if let lhs = lhs as? [AnyHashable: Any], let rhs = rhs as? [AnyHashable: Any], lhs.count == rhs.count {
    return lhs.allSatisfy { $1 == rhs[$0] }
  }
  // (Any...)
  else {
    let ml = Mirror(reflecting: lhs)
    let mr = Mirror(reflecting: rhs)
    guard ml.children.count == mr.children.count else {
      return false
    }
    return zip(ml.children, mr.children).allSatisfy { $0.value == $1.value }
  }
}

例如:

let t1 = (1, "2", 3.0, ["4" : 5], 6..<7, 8, [9, 10])
let t2 = (1, "2", 3.0, ["4" : 5], 6..<7, 8, [9, 10])
t1 == t2
© www.soinside.com 2019 - 2024. All rights reserved.