在 Swift 3 中,比较两个闭包的方法是什么?



假设你在 Swift 3 中有两个(Int)->()类型的闭包,并测试它们是否彼此相同:

typealias Baz = (Int)->()
let closure1:Baz = { print("foo ($0)") }
let closure2:Baz = { print("bar ($0)") }
if(closure1 == closure2) {
print("equal")
}

这无法编译,给出消息:

二元运算符 '==' 不能应用于两个 '(Int(->((' 操作数

好吧,好吧,那么我们如何比较两个相同类型的闭包,看看它们是否相同?

我很确定没有办法确定两个闭包是否相等。

显然,逻辑相等性检查是不可能的。这相当于找到停止问题的答案。(只需测试一下,看看你的代码是否等效于一段永远循环的代码。如果是,它不会停止。如果不是,它确实会停止。

理论上,您可能希望===运算符测试两个闭包是否是完全相同的代码段,但是当我在 Playground 中尝试时,这会出错。

Playground execution failed: error: MyPlayground.playground:1:20: error: cannot check reference equality of functions; operands here have types '(Int) ->  ()' and '(Int) -> ()'
let bar = closure1 === closure2
~~~~~~~~ ^   ~~~~~~~~

经过考虑,我确信这不起作用的原因是您无法确定闭包是否真的相等。闭包不仅是代码,还包括创建闭包的上下文,包括任何捕获。您无法检查相等的原因是,没有有意义的方式使两个闭包相等。

若要了解为什么捕获很重要,请查看以下代码。

func giveMeClosure(aString: String) -> () -> String
{
return { "returning " + aString }
}
let closure1 = giveMeClosure(aString: "foo")
let closure2 = giveMeClosure(aString: "bar")

closure1closure2平等吗?它们都使用相同的代码块

print(closure1()) // prints "returning foo"
print(closure2()) // prints "returning bar"

所以他们是不平等的。您可能会争辩说,您可以检查代码是否相同并且捕获是否相同,但是呢

func giveMeACount(aString: String) -> () -> Int
{
return { aString.characters.count }
}
let closure3 = giveMeACount(aString: "foo")
let closure4 = giveMeACount(aString: "bar")
print(closure3()) // prints 3
print(closure4()) // prints 3

显然,这些关闭是平等的。不可能实现任何适用于所有情况的合理平等定义,因此苹果甚至没有尝试过。这比提供在某些情况下错误的不完整实现更安全。

如果你想跟踪自己的闭包,将它们用作字典键等,你可以使用这样的东西:

struct TaggedClosure<P, R>: Equatable, Hashable {
let id: Int
let closure: (P) -> R
static func == (lhs: TaggedClosure, rhs: TaggedClosure) -> Bool {
return lhs.id == rhs.id
}
var hashValue: Int { return id }
}
let a = TaggedClosure(id: 1) { print("foo") }
let b = TaggedClosure(id: 1) { print("foo") }
let c = TaggedClosure(id: 2) { print("bar") }
print("a == b:", a == b) // => true
print("a == c:", a == c) // => false
print("b == c:", b == c) // => false

最新更新