Как определить троичный оператор в Scala, который сохраняет ведущие токены? - PullRequest
8 голосов
/ 24 апреля 2010

Я пишу генератор кода, который выдает вывод Scala.

Мне нужно эмулировать троичный оператор таким образом, чтобы токены вели к '?' остаются нетронутыми.

например. преобразовать выражение c ? p : q в c something. Простое if(c) p else q не соответствует моим критериям, так как требует поставить if( перед c.

Моя первая попытка (все еще использующая c / p / q, как указано выше)

c match { case(true) => p; case _ => q }

я нашел еще один вариант:

class ternary(val g: Boolean => Any) { def |: (b:Boolean) = g(b) }

implicit def autoTernary (g: Boolean => Any): ternary = new ternary(g)

, что позволяет мне написать:

c |: { b: Boolean => if(b) p else q }

Мне нравится общий вид второго варианта, но есть ли способ сделать его менее многословным?

Спасибо

Ответы [ 4 ]

14 голосов
/ 07 августа 2011

Давайте сделаем это просто:

Java:

tmp = (a > b) ? a : b;

Scala:

tmp = if (a > b) a else b

Помимо простоты, он понятен и быстр, потому что: не выделяет ненужные объекты, исключает сборщик мусора из уравнения (как это всегда должно быть) и лучше использует кэши процессоров.

14 голосов
/ 25 апреля 2010

Даже если синтаксис не оценивается в ожидаемом порядке - он связывает условное обозначение с первым вариантом! - вы можете создать свой собственный троичный оператор, например так:

class IfTrue[A](b: => Boolean, t: => A) { def |(f: => A) = if (b) t else f }
class MakeIfTrue(b: => Boolean) { def ?[A](t: => A) = new IfTrue[A](b,t) }
implicit def autoMakeIfTrue(b: => Boolean) = new MakeIfTrue(b)

Хитрость заключается в том, чтобы интерпретировать ? как метод объекта MakeIfTrue, который связывает условие с объектом для возврата в «истинном» случае. Полученный объект IfTrue теперь использует метод | в качестве запроса для оценки условия, возвращая сохраненную опцию true, если условие истинно, или только что переданную, если она ложна.

Обратите внимание, что я использовал такие вещи, как => A вместо просто A - параметры по имени - чтобы не вычислять выражение, если оно на самом деле не используется. Таким образом, вы будете оценивать только ту сторону, которая вам действительно нужна (точно так же, как утверждение if).

Давайте посмотрим на это в действии:

scala> List(1,3,2).isEmpty ? "Empty" | "Nonempty"
res0: java.lang.String = Nonempty

scala> (4*4 > 14) ? true | false
res1: Boolean = true

scala> class Scream(s: String) { println(s.toUpperCase + "!!!!") }
defined class Scream

scala> true ? new Scream("true") | new Scream("false")
TRUE!!!!
res3: Scream = Scream@1ccbdf7

(P.S. Чтобы избежать путаницы с библиотекой Actor ?, вы, вероятно, должны называть это как-то еще как |?.)

4 голосов
/ 25 апреля 2010

Вы можете использовать что-то вроде этого

sealed trait TernaryOperand[A] {
  def >(q: => A): A
}

case class TernarySecond[A](val p: A) extends TernaryOperand[A] {
  def >(q: => A) = p
}

case class TernaryThird[A]() extends TernaryOperand[A] {
  def >(q: => A) = q
}

implicit def ternary(c: Boolean) = new {
  def ?[A](p: => A): TernaryOperand[A] = if (c) TernarySecond(p) else TernaryThird()
}

val s1 = true ? "a" > "b"
println(s1) //will print "a"

val s2 = false ? "a" > "b"
println(s2) //will print "b"

Этот код преобразует любое логическое значение в анонимный тип, имеющий метод с именем ?. В зависимости от значения логического значения, этот метод возвратит либо TernarySecond, либо TernaryThird. У них обоих есть метод с именем >, который возвращает второй или третий операнд соответственно.

0 голосов
/ 14 ноября 2014

Тернарный оператор, который добавляет мое улучшение к лучшим реализациям Рекса Керра и Мишеля Крамера:

  • Мое улучшение - использовать новый класс значений Scala, чтобы избежать накладных расходов на бокс.
  • Вызовите имя по второму и третьему операндам, чтобы оценивался только выбранный.
  • Значение вызова Мишеля для 1-го (логического) операнда, чтобы избежать издержек по имени; это всегда оценивается.
  • Конкретный класс Рекса для условия, позволяющего избежать накладных расходов анонимного класса.
  • Мишель оценивает условие, чтобы определить, какой класс создать, чтобы избежать издержек конструктора с двумя аргументами.

.

sealed trait TernaryResult[T] extends Any {
  def |(op3: => T): T
}

class Ternary2ndOperand[T](val op2: T) extends AnyVal with TernaryResult[T] {
  def |(op3: => T) = op2
}

class Ternary3rdOperand[T](val op2: T) extends AnyVal with TernaryResult[T] {
  def |(op3: => T) = op3
}

class Ternary(val op1:Boolean) extends AnyVal {
   def ?[A](op2: => A): TernaryResult[A] = if (op1) new Ternary2ndOperand(op2) else new Ternary3rdOperand(op2)
}

object Ternary {
   implicit def toTernary(condition: Boolean) = new Ternary(condition)
}

Обратите внимание, что улучшение по сравнению с if else - это не только сохранение 6 символов. Так как цветовая подсветка синтаксиса Scala IDE для ключевых слов одинакова (например, фиолетовая) для if, else, null и true, в некоторых случаях улучшается контраст (что не показано в приведенной ниже цветовой подсветке синтаксиса). как в настоящее время отображается на этом сайте):

if (cond) true else null
cond ? true | null
...