In Swift 3, what is a way to compare two closures?

后端 未结 2 1425
死守一世寂寞
死守一世寂寞 2020-12-07 01:12

Suppose you have two closures of type (Int)->() in Swift 3 and test to see if they are the same as each other:

typealias Baz = (Int)->()
l         


        
相关标签:
2条回答
  • 2020-12-07 01:24

    I'm pretty sure there is no way to determine if two closures are equal.

    Obviously, a logical equality check is out of the question. That would be equivalent to finding an answer to the halting problem. (Just test to see if your code is equivalent to a piece of code that loops forever. If it is, it doesn't halt. If it isn't, it does halt.)

    In theory you might expect the === operator to test if two closures are the exact same piece of code, but that gives an error when I try it in 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
              ~~~~~~~~ ^   ~~~~~~~~
    

    Having thought about it, I'm sure the reason why that doesn't work is because you can't be sure that the closures really are equal. A closure is not just the code, but also the context in which it was created including any captures. The reason you can't check for equality is that there is no meaningful way in which two closures are equal.

    To understand why thew captures are important, look at the following code.

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

    Are closure1 and closure2 equal? They both use the same block of code

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

    So they are not equal. You could argue that you can check the code is the same and the captures are the same, but what about

    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
    

    Apparently these closures are equal. It's not possible to implement any reasonable definition of equality that will work in every case, so Apple has instead not even tried. This is safer than providing an incomplete implementation that is wrong in some cases.

    0 讨论(0)
  • 2020-12-07 01:31

    In the case where you want to track your own closures, uses them as Dictionary keys, etc., you can use something like this:

    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
    
    0 讨论(0)
提交回复
热议问题