2017-05-04 1 views
8

私が最近Pythonに触れると、X if C else Yという形で、その可読性を理解することを学びました。Swift 3でのPythonスタイルの条件式

古典的なternary conditional operator ?:が最初の引数として条件を持つとき、割り当てはすべて選択に関するものだと感じます。複数の3項演算子を入れ子にしてみると醜いです...条件が最初の式の後に移動すると、関数の数学的定義のように感じます。私は時にはコードの明確さに役立つことがわかります。

code kataとして、私はSwiftでpythonスタイルの条件式を実装したかったのです。それは私に必要な構文を得ることができる唯一のツールがカスタム演算子であるように思えます。それらは記号で構成されていなければなりません。 Math symbols in UnicodeラベルTRUE などの論理からdouble turnstileシンボルとバージョンを消さので、私は==||!=と一緒に行きました... 真実ではありません。これまでのところ、正しい優先度を見つけるためにしばらくのために戦った後、私はこれを持っている:

// 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 

あなたが空白で創造的な取得すると、それも少しニシキヘビを感じている:

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

私は二autoclosureがエスケープされなければならないことを好きではありません。 Nate Cook's answer about custom ternary operators in Swift 2は、もはやSwift 3にはないカリングシンタックスを使用していました...そして、私はそれも技術的にもエスケープクロージャであったと思います。

閉鎖をエスケープしないでこの作業を行う方法はありますか? それは問題ですか?たぶんSwiftコンパイラはコンパイル時にこの問題を解決するのに十分なほどスマートなので、実行時の影響はありません。

答えて

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

ニース。おそらく、その2番目の関数本体をもっときれいにすることができます:https://gist.github.com/woolsweater/4da0dc0bb348fb40a2becb1aec9a2890 –

+0

フィードバックをいただきありがとうございます。私は私の答えをクリーンアップします。 –

+2

最初の関数本体は 'return rhs ?? lhs() ' –

0

私は、Pythonのような三項演算子を形成する方法に良い答えを持っていないが、私は空白で組み込みの三項演算子は非常に論理的な方法で連鎖させることができることを指摘したいです。

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

が、これは明らかではなかったのpythonの背景から来る:チェーン三項演算子に変換

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

この与える:

if elseブロックとたとえば、あなたがこのような何かを書くことができ最初は他の人と共有したいと思っています。