?に類似した三項演算子:


94

私はこのような構成を避けようとしています:

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

この例では、thenand elseブランチは単純ですが、複雑なブランチをイメージ化することができます。私は以下を構築しました:

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}

コンパイラーは型を推測するために追加のものが必要だと思います。


実際にはこれが私の答えに含まれていなかったため、問題が発生するのは、型推論が左から右に最もよく機能するが、演算子の優先順位のため、トークンを右から左にバインドしているためです。すべてのステートメントを(優先順位が同じ)単語にして、グループ化の方法を変更すると、必要な推論が得られます。(あなたが持っているでしょうすなわちHasIsIsWithConditionConditionAndTrueCase左から右への式の部分を構築することになるクラス。)
レックスカー

私は無意識のうちに、左から右への型推論の方法を推定し、しかし、メソッド名の演算子の優先順位と結合規則でstucked特に始まる?メソッド名最初の文字として、他のalphanumの文字の前と:左結合性のために。したがって、左から右に型推論が機能するように、新しいメソッド名を再考する必要があります。ありがとう!
Peter Schmitz

回答:


28

先頭のトークンを保持するScalaで三項演算子を定義する方法を組み合わせることができますか?回答とされるオプションは、値が良好なパターンを包みますか?取得するため

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

これで十分ですか?


それは私が考えていることに非常に近いです。素敵なアプローチ。それについて考えます。最初のコードを回避する理由valは、次のifステートメントの一時的なものがないようにするために、より簡潔にするためです。
Peter Schmitz、2011

125

以下からのトニー・モリスのラムダブログ

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

これは好ましくない場合があります。おそらく、Javaと同じ構文を使用して記述したいでしょう。残念ながらできません。これは、:が有効な識別子ではないためです。恐れることはない|です!これで落ち着きますか?

c ? p | q

次に、次のコードが必要になります。=>引数の名前による呼び出し()アノテーションに注意してください。この評価戦略は、Javaの三項演算子を正しく書き換えるために必要です。これはJava自体では実行できません。

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

先ほど定義した新しい演算子を使用した例を次に示します。

object T {   val condition = true

  import Bool._

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

楽しんで ;)


はい、これを以前に見たことがありますが、違いは、最初の式の(評価された)値がthenand else節の引数としてあることです。
Peter Schmitz

5
私は if(c) p else qアプローチを採用しました...中かっこの欠如は私にタッチを不快にさせますが、それは単なるスタイルのことです
rjohnston

17

基本的なScalaで表現されたRex Kerrの答え

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

ただし、if-else構文のどの部分を最適化するかはわかりません。


非常にまっすぐな方法。日常的に使用するmatch / caseステートメントについて忘れることがあります。私は一行の三項if then elseイディオムに固執しましたが、それは確かに解決するためのわかりやすい方法です。
Peter Schmitz

1
パターンマッチングは、3つ以上のブランチに簡単に拡張できます。
ラファエル


0

:常にバックティック:でエスケープすることに問題がない限り、それ自体は有効な演算子ではないため、「|」などの別の文字を使用できます。上記の回答の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

もちろん、値がリストの場合、これは::演算子自体を持っているため機能しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.