Swift 3中的Python样式条件表达式

Pal*_*ndo 8 swift

在我最近接触Python之后,我学会了以其形式欣赏其条件表达式的可读性X if C else Y.

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

作为代码kata,我想在Swift中实现python样式的条件表达式.似乎唯一可以让我获得所需语法的工具是自定义运算符.它们必须由符号组成.Unicode中的数学符号将逻辑上的双旋转符号标记为TRUE ?,划掉的版本不正确?...所以我选择了==||!=.到目前为止,在争取一段时间找到正确的优先权后,我有这样的:

// 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)
}
Run Code Online (Sandbox Code Playgroud)

我知道结果看起来不是很流畅或特别易读,但从好的方面来看,即使表达式分布在多行上,这些运算符也能正常工作.

let e = // is 12 (5 + 7)
    1 + 3 ==| false |!=
    5 + 7 ==| true |!=
    19 + 23
Run Code Online (Sandbox Code Playgroud)

当你用空白创作时,它甚至感觉有点pythonic :

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

我不喜欢第二个autoclosure必须逃脱.Nate Cook关于Swift 2中自定义三元运算符的回答使用了currying语法,这已不再是Swift 3 ......而且我认为这在技术上也是一个逃避关闭.

有没有办法在没有逃避关闭的情况下完成这项工作? 它甚至重要吗?也许Swift编译器足够聪明,可以在编译期间解决这个问题,因此它没有运行时影响?

Gar*_*kin 7

好问题:)

如果条件为假,则将结果存储为可选,而不是存储表达式.根据一些评论编辑,产生一些更清洁的代码.

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()
}
Run Code Online (Sandbox Code Playgroud)

  • 第一个函数体可以写成`return rhs ?? LHS()` (2认同)