自定义中缀运算符和可选项

Custom infix operators and optionals

class TreeNode: Equatable {
    static func ==(lhs: TreeNode, rhs: TreeNode) -> Bool {
        lhs.val == rhs.val && lhs.left == rhs.right && lhs.right == rhs.left
    }
    
    var val: Int = 0
    var left, right: TreeNode?
}

这段代码可以编译甚至可以工作。但为什么? leftright 变量是可选的,我不是应该先在 static func == 的正文中解包吗?

实际上这不是一个等式。如您所见,它更像是某种对称方程。因此,我想为此目的定义具有不同名称的自定义运算符:

infix operator =|=: ComparisonPrecedence
class TreeNode {
    static func =|=(lhs: TreeNode, rhs: TreeNode) -> Bool {
        lhs.val == rhs.val && lhs.left =|= rhs.right && lhs.right =|= rhs.left
    }
    
    var val: Int = 0
    var left, right: TreeNode?
}

由于我之前提到的原因,现在它无法编译。它要我先打开选项。
实际上,如果它像“==”)那样“正常工作”会很棒))因为不必显式地解开可选项在这里会很方便。

所以我想了解为什么它在这两种情况下表现不同。

This code compiles and even works. But why?

只是因为有一个 == operator 为所有 Optional<Wrapped> 声明,其中 WrappedEquatable,像这样:

static func == (lhs: Wrapped?, rhs: Wrapped?) -> Bool

TreeNode 在您的第一个代码片段中是 Equatable,所以它有效。

在您的第二个代码片段中,您没有声明对两个 TreeNode? 进行运算的 =|= 运算符。您可以通过将其置于全局范围内来做到这一点...

func =|= (lhs: TreeNode?, rhs: TreeNode?) -> Bool {
    switch (lhs, rhs) {
    case (nil, nil): // both nil
        return true
    case (let x?, let y?): // both non-nil
        return x =|= y // compare two non-optional tree nodes
    default:
        return false
    }
}

或编写 Optional 扩展名:

extension Optional where Wrapped == TreeNode {
    static func =|= (lhs: Wrapped?, rhs: Wrapped?) -> Bool {
        switch (lhs, rhs) {
        case (nil, nil): // both nil
            return true
        case (let x?, let y?): // both non-nil
            return x =|= y // compare two non-optional tree nodes
        default:
            return false
        }
    }
}

但正如 Leo Dabus 所说,我只是遵循 Equatable 而不是创建您自己的运算符。符合现有协议允许您将 TreeNode 与标准库中的许多 API 一起使用,例如 Array.contains(_:).