Термінальний оператор, подібний до?:


94

Я намагаюся уникати таких конструкцій:

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

Гаразд, у цьому прикладі thenі 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}

Я думаю, компілятору потрібні додаткові матеріали для висновку про типи.


Оскільки я насправді цього не мав у своїй відповіді - причина, через яку у вас виникають проблеми, полягає в тому, що висновок типу найкраще працює зліва направо, але ви зв’язуєте свої маркери справа наліво через перевагу оператора. Якщо ви зробите всі свої висловлювання словами (з однаковим пріоритетом) і зміните спосіб групування речей, ви отримаєте бажане висновок. (Тобто ви б HasIs, IsWithCondition, ConditionAndTrueCaseкласи , які будуть нарощуватися частини виразу зліва направо.)
Рекс Керр

Я несвідомо припускав спосіб виведення типу зліва направо, але дотримувався пріоритету оператора та асоціативності назв методів, особливо починаючи з ?будь-якого іншого алфавітного символу як імені методу first char та a :для лівої асоціативності. Тому мені доведеться переосмислити нові імена методів, щоб зробити висновок про тип роботи зліва направо. Дякую!
Пітер Шмітц

Відповіді:


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висловлювання: зробіть це зрозумілим в одному рядку, як і один, маючи це на увазі.
Пітер Шмітц

125

З блогу "Ламбда" Тоні Морріса :

Я багато чую це запитання. Так. Замість c ? p : qцього написано if(c) p else q.

Це може бути не кращим. Можливо, ви хочете написати це з використанням того ж синтаксису, що і Java. На жаль, ви не можете. Це тому :, що не є дійсним ідентифікатором. Не бійся, |є! Ви б з цим погодилися?

c ? p | q

Тоді вам знадобиться такий код. Зверніть увагу на =>анотації call-by-name ( ) на аргументах. Ця стратегія оцінювання потрібна для правильного перезапису термінального оператора 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"
}

Веселіться;)


так, я бачив це раніше, але різниця полягає в тому, що у мене є (оцінене) значення мого першого вираження як аргумент в thenі elseпункті.
Пітер Шмітц

5
Я взяв if(c) p else qпідхід ... відсутність брекетів робить мені дотик незручним, але це просто стиль стилю
rjohnston

17

Відповідь Рекса Керра, висловлена ​​в базовій шкалі:

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

хоча я не впевнений, яку частину конструкції if – else ви хочете оптимізувати.


дуже прямий шлях. іноді забувається про щоденне використання матчу / випадок справи. Я просто дотримувався потрійної if then elseідіоми в одному рядку , але це справді зрозумілий спосіб вирішити.
Пітер Шмітц

1
Ваги легко узгоджують візерунок на більш ніж двох гілках.
Рафаель


0

Оскільки: сам по собі не буде дійсним оператором, якщо ви не в змозі завжди уникати його за допомогою зворотних галочок :, ви можете використовувати інший символ, наприклад "|" як в одній з відповідей вище. А як щодо елвіса з козочкою? ::

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

Звичайно, це знову не спрацює, якщо значення є списками, оскільки вони мають оператор ::.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.