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

时间:2015-05-23 13:04:41

标签: swift collections standard-library

我在两个元组数组上使用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?!?!

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

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

3 个答案:

答案 0 :(得分:3)

无法确定这是一个错误还是我不理解的东西(可疑的错误,但需要更多地使用它)。

但是,这是平均时间的一种解决方法,它涉及对数据进行完全解构:

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) {
    $0.0 == $1.0 && Int($0.1) == $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($0) }
}
// now the above use of equal will compile and return the correct result

P.S。您可能希望为UInt转化

添加溢出检查

答案 1 :(得分:2)

为了支持@Airspeed Velocity,它似乎是一个bug,因为你只能定义第一个元组,而第二个元组将按预期工作: - / Xcode 6.3.2 Swift 1.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 

答案 2 :(得分:1)

是的,看起来像个臭虫。奇怪的是,如果你用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
  }

}