【问题标题】:Python style conditional expression in Swift 3Swift 3 中的 Python 风格条件表达式
【发布时间】:2017-12-16 06:04:08
【问题描述】:

在我最近接触 Python 之后,我学会了欣赏其 conditional expressions 的可读性,其形式为 X if C else Y

当经典的ternary conditional operator ?: 将条件作为第一个参数时,感觉就像赋值完全是关于选择的。当您尝试嵌套多个三元运算符时,它会变得很难看……当条件在第一个表达式之后移动时,感觉更像是函数的数学定义。我发现这有时有助于代码清晰。

作为code kata,我想在Swift 中实现python 风格的条件表达式。似乎唯一能让我获得所需语法的工具是自定义运算符。它们必须由符号组成。 Math symbols in Unicode 将逻辑中的 double turnstile 符号标记为 TRUE 并且划掉的版本不是 TRUE ……所以我选择了 @987654330 @|!=。到目前为止,在为找到正确的优先级而奋斗了一段时间之后,我有这个:

// Python-like conditional expression

struct CondExpr<T> {
    let cond: Bool
    let expr: () -> T
}

infix operator ==| : TernaryPrecedence // if/where
infix operator |!= : TernaryPrecedence // else

func ==|<T> (lhs: @autoclosure () -> T, rhs: CondExpr<T>) -> T {
    return rhs.cond ? lhs() : rhs.expr()
}

func |!=<T> (lhs: Bool, rhs: @escaping @autoclosure () -> T) -> CondExpr<T> {
    return CondExpr<T>(cond: lhs, expr: rhs)
}

我知道结果看起来不是很 swifty 或特别可读,但从好的方面来说,即使表达式分布在多行上,这些运算符也可以工作。

let e = // is 12 (5 + 7)
    1 + 3 ==| false |!=
    5 + 7 ==| true |!=
    19 + 23

当你用空白来创造创意时,它甚至感觉有点pythonic ???? :

let included =
    Set(filters)             ==| !filters.isEmpty |!=
    Set(precommitTests.keys) ==| onlyPrecommit    |!=
    Set(allTests.map { $0.key })

我不喜欢第二个自动关闭必须逃脱。 Nate Cook's answer about custom ternary operators in Swift 2 使用了 Swift 3 中不再存在的柯里化语法……我猜这在技术上也是一个转义闭包。

有没有办法在不逃避关闭的情况下完成这项工作? 这还重要吗?也许 Swift 编译器足够聪明,可以在编译时解决这个问题,从而不会影响运行时?

【问题讨论】:

    标签: swift


    【解决方案1】:

    好问题:)

    如果条件为假,则将结果存储为可选项,而不是存储表达式。基于一些 cmets 进行编辑,产生了一些更简洁的代码。

    infix operator ==| : TernaryPrecedence // if/where
    infix operator |!= : TernaryPrecedence // else
    
    func ==|<T> (lhs: @autoclosure () -> T, rhs: T?) -> T {
        return rhs ?? lhs()
    }
    
    func |!=<T> (lhs: Bool, rhs: @autoclosure () -> T) -> T? {
        return lhs ? nil : rhs()
    }
    

    【讨论】:

    【解决方案2】:

    对于如何形成像 Python 那样的三元运算符,我没有很好的答案,但我确实想指出,使用空格,内置三元运算符可以以非常合乎逻辑的方式链接。

    例如使用if else 块,您可以编写如下内容:

    //assuming the return value is being assigned to variable included
    if !filters.isEmpty {
        return Set(filters)
    } else if onlyPrecommit {
        return Set(precommitTests.keys)
    } else {
        return Set(allTests.map { $0.key })
    }
    

    转换为三元运算符链式给出:

    let included =( !filters.isEmpty ? Set(filters)
                   : onlyPrecommit   ? Set(precommitTests.keys)
                                     : Set(allTests.map { $0.key })
                  )
    

    来自 python 背景,起初这并不明显,所以我想与其他人分享。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-20
      相关资源
      最近更新 更多