Swift 3中的Python风格条件表达式

8
在最近接触Python后,我开始欣赏它的条件表达式的可读性,形式为X if C else Y
当经典的三目运算符?:将条件作为第一个参数时,它感觉像是赋值全部都是关于选择的。当你尝试嵌套多个三目运算符时,它变得很丑陋... 当条件移动到第一个表达式之后时,它更像是函数的数学定义。我发现这有时可以帮助提高代码清晰度。

作为代码编程训练的一员,我想在Swift中实现Python风格的条件表达式。似乎唯一能给我所需语法的工具是自定义运算符。它们必须由符号组成。Unicode中的数学符号将逻辑中的双turnstile符号标记为TRUE ,其被划掉的版本是NOT 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)
}

我知道结果看起来不太流畅或易读,但好的一面是,即使表达式跨越多行,这些运算符也能正常工作。

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

当您在空格方面有创意时,它甚至会感觉有点类似于Python编程语言。
let included =
    Set(filters)             ==| !filters.isEmpty |!=
    Set(precommitTests.keys) ==| onlyPrecommit    |!=
    Set(allTests.map { $0.key })

我不喜欢第二个自动闭包必须进行转义。 Nate Cook在Swift 2中自定义三元运算符的回答使用了柯里化语法,但在Swift 3中已经不再使用... 我猜那也是一个需要转义的闭包。

有没有办法在不使用转义闭包的情况下使其工作?这真的很重要吗?也许Swift编译器足够聪明,在编译时解决它,所以它不会在运行时产生影响?

2个回答

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()
}

1
不错。你可以让第二个函数体看起来更漂亮,比如这样:https://gist.github.com/woolsweater/4da0dc0bb348fb40a2becb1aec9a2890 - jscs
2
第一个函数体可以写成 return rhs ?? lhs() - Martin R
非常好!它仍然像以前一样短路。(我很担心这个... ‍♂️) - Palimondo

0

我没有一个好的答案来形成像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 的编程背景,所以一开始并不明显,所以我想与其他人分享。


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