2011-02-09 48 views
73

私はこのような構造を避けるためにしようとしています:三項演算子?:

val result = this.getClass.getSimpleName 
if (result.endsWith("$")) result.init else result 

[OK]を、この例ではthenelseブランチは簡単ですが、あなたは画像の複雑なものをすることができます。 は、私は次のように構築された:

私は上記の簡単な例を置き換えることができ、という定義
object TernaryOp { 
    class Ternary[T](t: T) { 
    def is[R](bte: BranchThenElse[T,R]) = if (bte.branch(t)) bte.then(t) else bte.elze(t) 
    } 
    class Branch[T](branch: T => Boolean) { 
    def ?[R] (then: T => R) = new BranchThen(branch,then) 
    } 
    class BranchThen[T,R](val branch: T => Boolean, val then: T => R) 
    class Elze[T,R](elze: T => R) { 
    def :: (bt: BranchThen[T,R]) = new BranchThenElse(bt.branch,bt.then,elze) 
    } 
    class BranchThenElse[T,R](val branch: T => Boolean, val then: T => R, val elze: T => R) 
    implicit def any2Ternary[T](t: T) = new Ternary(t) 
    implicit def fct2Branch[T](branch: T => Boolean) = new Branch(branch) 
    implicit def fct2Elze[T,R](elze: T => R) = new Elze(elze) 
} 

this.getClass.getSimpleName is {s: String => s.endsWith("$")} ? {s: String => s.init} :: {s: String => s} 

しかし、どのように私はs: String =>を取り除くことができますか?私はそのようなものが欲しい:

this.getClass.getSimpleName is {_.endsWith("$")} ? {_.init} :: {identity} 

私は推測するには、コンパイラは種類を推測する余分なものが必要だと思います。

scala> "Hi".getClass.getSimpleName |> {x => x.endsWith("$") ? x.init | x} 
res0: String = String 

scala> List.getClass.getSimpleName |> {x => x.endsWith("$") ? x.init | x} 
res1: String = List 

を取得するために

+0

を - あなたの「理由問題は左から右にタイプ推論が最適ですが、オペレータの優先順位のためにトークンを右から左にバインドしていることです。あなたの言葉をすべて同じ言葉で(同じ優先順位で)作って、一緒にグループ化する方法を変えれば、あなたが望む推論を得ることができます。 (つまり、左から右へ式の一部を構築する 'HasIs'、' IsWithCondition'、 'ConditionAndTrueCase'クラスがあります) –

+0

私は無意識のうちに型推論の方法を左から右に推測しましたが、演算子の優先順位他の英数字の前に '?'で始まり、メソッド名の最初のcharとして、そして左の連想のために ':'で始まるメソッド名の連想性。だから、タイプ推論を左から右へ動かすために、新しいメソッド名を考え直さなければならない。ありがとう! –

答えて

20

我々は、これがあなたのニーズに適してIs Option wrapping a value a good pattern?に答えてHow to define a ternary operator in Scala which preserves leading tokens?を組み合わせることができますか?

+0

私が心に留めていることに非常に近いです。良いアプローチ。私はそれについて考えます。非常に最初のコードを避ける私の理由は、次の 'if'文のための一時的な' val'を持たないという点でより簡潔であることでした:1つの行で分かりやすくしてください。 –

13

Rex Kerr’s answerは、基本的なScalaで表現:

"Hi".getClass.getSimpleName match { 
    case x if x.endsWith("$") => x.init 
    case x => x 
} 

を私はあなたが最適化したい構築のif-elseのどの部分かわからないが。

+0

非常にストレートです。時には、日常的なマッチ/ケースステートメントについて忘れてしまうこともあります。私はちょうど1行の三項「それ以外の場合は」イディオムに固執しましたが、それは本当に解決するための分かりやすい方法です。 –

+0

パターンマッチングは、2つ以上の枝に容易に拡大縮小されます。 – Raphael

100

Tony Morris' Lambda Blogから:

私はこの質問をたくさん聞きます。はい、そうです。 c ? p : qの代わりに、if(c) p else qと書かれています。

これはあまり好ましくありません。おそらく、Javaと同じ構文を使用して記述したいと思うかもしれません。悲しいことに、あなたはできません。これは、:が有効な識別子ではないためです。恐れていない、|です!これで解決しますか?

c ? p | q 

次に、次のコードが必要です。引数の名前によるコール(=>)の注釈に注目してください。この評価方法は、Javaの三項演算子を正しく書き換えるために必要です。これはJava自体ではできません。あなたがOKでない限り、それ自体で有効な演算子ではありません。ので)

+0

はい、前に見ましたが、違いは、 'then'節と' else'節で最初の式の(評価された)値が引数としてあることです。 –

+0

@Imre:リンクを修正してコンテンツをコピーしました。 – Landei

+2

私は 'if(c)p else q'アプローチを取った...中括弧の欠如は私に不快感を与えるが、それはちょうどスタイルのことだ – rjohnston

0

;

object T { val condition = true 

    import Bool._ 

    // yay! 
    val x = condition ? "yes" | "no" 
} 

は楽しいし:ここで

case class Bool(b: Boolean) { 
    def ?[X](t: => X) = new { 
    def |(f: => X) = if(b) t else f 
    } 
} 

object Bool { 
    implicit def BooleanBool(b: Boolean) = Bool(b) 
} 

は我々だけで定義されたnew演算子を使用した例です。いつもバックチック :でそれをエスケープして、あなたは別のキャラクターと行くことができます。 "|"上記の答えの1つのように。彼らは::オペレータ自身を持っているので、あなたの値は、リストされている場合しかし、どのようにあごひげを持つエルビスでしょうか?もちろん::

implicit class Question[T](predicate: => Boolean) { 
    def ?(left: => T) = predicate -> left 
} 
implicit class Colon[R](right: => R) { 
    def ::[L <% R](pair: (Boolean, L)): R = if (q._1) q._2 else right 
} 
val x = (5 % 2 == 0) ? 5 :: 4.5 

これは再び動作しません。