2010-04-24 21 views
8

Estoy escribiendo un generador de código que produce salida Scala.¿Cómo se define un operador ternario en Scala que conserva los tokens principales?

Necesito emular un operador ternario de tal manera que los tokens que conducen a '?' permanece intacto.

p. Ej. convierta la expresión c ? p : q en c something. El simple if(c) p else q falla mis criterios, ya que requiere poner if( antes de c.

Mi primer intento (todavía mediante c/p/q que el anterior) se

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

otra opción que encontré fue:

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

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

la que me permite escribir:

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

Me gusta el aspecto general de la segunda opción, pero ¿hay alguna manera de hacerlo menos detallado?

Gracias

+0

por ejemplo algo que use '_' en lugar de b: Boolean sería bueno, pero no pude hacer que nada funcione bien –

+1

Ver 'BooleanW #?' de Scalaz: http://scalaz.googlecode.com/svn/continuous/latest /browse.sxr/scalaz/BooleanW.scala.html – retronym

Respuesta

14

A pesar de que la sintaxis no se evalúa en el orden esperado - que une el condicional a la primera opción - usted puede hacer su propio operador ternario así:!

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) 

El truco es interpretar ? como un método en un objeto MakeIfTrue que vincula la condición al objeto para devolver en el caso "verdadero". El objeto resultante IfTrue ahora usa el método | como una solicitud para evaluar la condición, devolviendo la opción verdadera almacenada si la condición es verdadera, o la que acaba de pasar una si es falsa.

Tenga en cuenta que he usado cosas como => A en lugar de solo A --por parámetros de nombre - para no evaluar la expresión a menos que realmente se use. Por lo tanto, solo evaluará el lado que realmente necesita (al igual que una instrucción if).

Vamos a verlo en acción:

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 = [email protected] 

(PS Para evitar confusiones con la biblioteca Actor ?, probablemente debería llamarlo de otra manera como |?.)

+0

Doh, demasiado lento. Estuve en el proceso de escribir algo similar a esto, solo para obtener el temido mensaje de "2 nuevas respuestas" –

+0

@Jackson: ¡Las alternativas interesantes aún valen la pena! –

+0

¡Agradable! Lo único que voy a cambiar es? a |? para obtener la prioridad más baja del operador. –

4

Usted podría utilizar algo como esto

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" 

Este código convierte cualquier valor booleano a un tipo anónimo que tiene un método llamado ?. Dependiendo del valor del booleano, este método devolverá TernarySecond o TernaryThird. Ambos tienen un método llamado > que devuelve el segundo operando o el tercero respectivamente.

14

Vamos a mantenerlo simple:

Java:

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

Scala:

tmp = if (a > b) a else b 

Además de la sencillez, es claro y rápido porque: no asignar objetos que no es necesario , mantiene el recolector de basura fuera de ecuación (como siempre debería ser) y hace un mejor uso de las cachés de procesador.

+1

Esta es mi respuesta favorita. Tu explicación es tan simple como la solución. – theJollySin

+2

Esto resuelve un problema diferente, no el indicado en la pregunta. –

0

operador ternario que se suma mi mejora en la medida de Rex Kerr y las implementaciones de Michel Krämer:

  • Mi mejora de usar nueva clase de valor del Scala para evitar la sobrecarga de boxeo.
  • Llamar por nombre en el 2º y 3er operandos para que solo se evalúe el elegido.
  • Llamada de Michel por valor en el primer operando (booleano) para evitar gastos generales por nombre; siempre es evaluado
  • Clase concreta de Rex para la condición de evitar cualquier sobrecarga de clase anónima.
  • Evaluación de Michel de la condición para determinar qué clase construir para evitar la sobrecarga de un constructor de dos argumentos.

.

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

Nota de la mejora con respecto a if else no son sólo los 6 caracteres que se almacenan. Con la coloración de sintaxis de Scala IDE en las palabras clave (p. Ej. Morado) para if, else, null y true, hay mejor contraste en algunos casos (que no se muestra con la siguiente coloración de sintaxis).

if (cond) true else null 
cond ? true | null 
Cuestiones relacionadas