为什么 Nil 合并运算符是右结合的?

5

它不应该是左结合的吗?

我认为

let a = b ?? c ?? d

被分组的方式类似于

let a = (b ?? c) ?? d

不是

let a = b ?? (c ?? d)

但它被声明为右结合。我是否有什么误解或遗漏了什么?
1个回答

7

我认为这是一种优化。左结合或右结合不会改变结果。

这个:

(b ?? c) ?? d

对于表达式b ?? c的求值结果,将其作为x ?? d左侧的操作数。即使b不为空,合并运算符也会被执行两次。

在这种情况下,可以考虑使用:

b ?? (c ?? d)

如果b不为nil,则右侧的表达式将不会被评估,因此不会执行。
补充说明 为了证明这一点,我进行了简单测试:重新定义了nil合并运算符。
infix operator !!! {
    associativity left
    precedence 110
}

func !!!<T>(optional: T?, defaultValue: @autoclosure () -> T?) -> T? {
    if let value = optional {
        println(optional)
        return value
    }

    let def = defaultValue()
    println(def)
    return def
}

使用这个测试数据:

let a: String? = "a"
let b: String? = "b"
let c: String? = "c"

let d = a !!! b !!! c

使用“左结合性”,这是打印到控制台的内容:
Optional("a")
Optional("a")

而将结合性更改为“right”时,输出结果如下:
Optional("a")

这意味着在使用右结合时,如果左边不是nil,则忽略运算符的右侧。

我认为在b ?? (c ?? d)中,应该先计算(c ?? d),再检查b是否为nil,因为它们在括号内。所以即使b不是nil,这些表达式也不会被计算。 - Pitiphong Phongpattranont
但我能理解你对这些表达式评估的观点,这是一个很好的观点。那么这是否意味着 Nil Coalescing 有特殊的评估规则? - Pitiphong Phongpattranont
2
我认为这不是一个特殊的评估规则。它适用于左结合和右结合,但在后一种情况下,它的执行更加优化。请参见更新的答案。 - Antonio
哇,非常感谢。我忘记考虑@autoclosure了。你的例子非常好,可以很好地解释这个问题。 - Pitiphong Phongpattranont
我使用了你的代码片段并删除了@autoclosure,结果与我预期的一样。 - Pitiphong Phongpattranont
为了完整起见,我提一下我们可以使用 f() ?? g() ?? h(),其中如果先调用 g,则 f 可能会抛出异常等。在这种情况下,顺序很重要。 - Codie CodeMonkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接