Swift zip 生成器只迭代一侧两次

Swift zip generator only iterates one side twice

我在两个元组数组上使用 zip。当我迭代结果时,结果元组仅包含左侧的元组。

有趣的是,Array(zip(...)) 按预期生成了一个集合。我想节省一些周期和内存,并且不想为了循环而生成一个新数组。

let expectation: [(String, UInt)] = [("bar", 0)]
let comparison: [(String, Int)] = [("foo", 0)]

func ==(lhs: [(String, UInt)], rhs: [(String, Int)]) -> Bool {
    if lhs.count != rhs.count {
        return false
    }

    for (l, r) in zip(lhs, rhs) {
        // Looking at `l` and `r` in lldb shows both are the same.
        if l.0 != r.0 || Int(l.1) != r.1 {
            return false
        }
    }

    return true
}

let equals = (expectation == comparison) // true?!?!

这是一种方便的方法,可以将测试替身中的函数调用记录与来自实际测试用例的测试数据进行比较。双记录 (String, UInt),在测试用例中键入元组会产生 (String, Int),所以我想:让我们创建一个简单的相等函数吧!将 UInt 更改为 Int 不会改变任何事情。

怎么样?渲染 zip 对我来说毫无用处(除非你能解释发生了什么)。

无法确定这是一个错误还是我不理解的地方(怀疑是错误但需要更多地尝试)。

但是,同时这里有一个解决方法,它涉及完全解构数据:

let expectation: [(String, UInt)] = [("bar", 0)]
let comparison: [(String, Int)] = [("foo", 1)]

func ==(lhs: [(String, UInt)], rhs: [(String, Int)]) -> Bool {
    if lhs.count != rhs.count {
        return false
    }

    for ((ls, li),(rs,ri)) in zip(lhs, rhs) {
        // Looking at `l` and `r` in lldb shows both are the same.
        if ls != rs || Int(li) != ri {
            return false
        }
    }

    return true
}

let equals = (expectation == comparison) // now returns false

理论上,这应该更容易写成:

equal(expectation, comparison) {
    [=11=].0 == .0 && Int([=11=].1) == .1
}

除了令人气愤的是,采用谓词的equal函数仍然要求两个序列的元素相同!雷达17590938.

针对此特定于数组的快速修复可能如下所示:

func equal<T,U>(lhs: [T], rhs: [U], isEquivalent: (T,U)->Bool) -> Bool {
    if lhs.count != rhs.count { return false }
    return !contains(zip(lhs, rhs)) { !isEquivalent([=12=]) }
}
// now the above use of equal will compile and return the correct result

p.s。您可能想为 UInt 转换

添加溢出检查

为了支持@Airspeed Velocity,这似乎是一个错误,因为您只能定义第一个元组,而第二个元组将按预期工作:-/ Xcode6.3.2Swift1.2

let expectation: [(String, UInt)] = [("bar", 0)]
let comparison: [(String, Int)] = [("foo", 1)]

func ==(lhs: [(String, UInt)], rhs: [(String, Int)]) -> Bool {
    if lhs.count != rhs.count {
        return false
    }

    for ((l0, l1), r) in zip(lhs, rhs) {
        // Explicitly detiled first tuple
        if l0 != r.0 || Int(l1) != r.1 {
            return false
        }
    }

    return true
}

let equals = (expectation == comparison) // false as expected 

是的,看起来像个错误。奇怪的是,如果用 contains() 替换 for 循环,则不需要解构元组,它的工作方式与您预期的一样:

func ==(lhs: [(String, UInt)], rhs: [(String, Int)]) -> Bool {
  if lhs.count != rhs.count {
    return false
  }

  return !contains(zip(lhs, rhs)) {
    l, r in l.0 != r.0 || Int(l.1) != r.1
  }

}