2010-04-24 10 views
8

Sto scrivendo un generatore di codice che produce l'uscita Scala.Come definire un operatore ternario in Scala che conserva i token principali?

Ho bisogno di emulare un operatore ternario in modo tale che i token che portano a "?" rimane intatto.

ad es. convertire l'espressione c ? p : q in c something. Il semplice if(c) p else q non soddisfa i miei criteri, poiché richiede il if( prima di c.

Il mio primo tentativo (ancora utilizzando c/p/q come sopra) è

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

un'altra opzione che ho trovato era:

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

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

che mi permette di scrivere:

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

Mi piace l'aspetto generale della seconda opzione, ma c'è un modo per renderlo meno dettagliato?

Grazie

+0

ad es. qualcosa usando '_' al posto di b: Boolean sarebbe bello, ma non riuscivo a far funzionare nulla proprio –

+1

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

risposta

14

Anche se la sintassi non valuta nell'ordine previsto - si lega la condizionale per la prima opzione - è possibile rendere il proprio operatore ternario in questo modo:!

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) 

Il trucco è interpretare ? come un metodo su un oggetto MakeIfTrue che associa la condizione all'oggetto da restituire nel caso "true". L'oggetto IfTrue risultante ora utilizza il metodo | come una richiesta per valutare la condizione, restituendo l'opzione true archiviata se la condizione è vera o quella appena passata se è falsa.

Nota che ho utilizzato elementi come => A anziché solo A - parametri di nome-in-base per non valutare l'espressione a meno che non sia effettivamente utilizzata. Pertanto, valuterai solo il lato di cui hai realmente bisogno (proprio come un'istruzione if).

Vediamolo in azione:

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 Per evitare confusione con la libreria Attore ?, probabilmente si dovrebbe chiamarlo qualcos'altro come |?.)

+0

Doh, troppo lento. Ero in procinto di digitare qualcosa di simile a questo, solo per ottenere il temuto "2 nuove risposte" messaggio –

+0

@Jackson: alternative interessanti sono ancora la pena! –

+0

Bello! L'unica cosa che cambierò è? a |? per ottenere la priorità più bassa dell'operatore. –

4

Si potrebbe usare qualcosa di simile

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" 

Questo codice converte qualsiasi valore booleano per un tipo anonimo che ha un metodo chiamato ?. A seconda del valore del valore booleano, questo metodo restituirà TernarySecond o TernaryThird. Entrambi hanno un metodo chiamato > che restituisce rispettivamente il secondo operando o il terzo.

14

teniamolo semplice:

Java:

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

Scala:

tmp = if (a > b) a else b 

Inoltre la semplicità, è chiaro e veloce perché: non allocare oggetti non è necessario , mantiene il garbage collector fuori dall'equazione (come dovrebbe sempre essere) e fa un uso migliore delle cache del processore.

+1

Questa è la mia risposta preferita. La tua spiegazione è semplice come la soluzione. – theJollySin

+2

Questo risolve un problema diverso, non quello indicato nella domanda. –

0

operatore ternario che aggiunge il mio miglioramento al meglio delle Rex Kerr e le implementazioni di Michel Krämer:

  • mio miglioramento di utilizzare nuova classe valore di Scala per evitare un sovraccarico di pugilato.
  • Chiamare per nome sul 2 ° e 3 ° operando in modo da valutare solo quello selezionato.
  • Chiamata in base al valore di Michel sul primo operando (Booleano) per evitare l'overhead del nome secondario; è sempre valutato
  • Classe concreta di Rex per la condizione per evitare qualsiasi sovraccarico di classe anonima.
  • Valutazione di Michel della condizione per determinare quale classe da costruire per evitare di sovraccarico di un costruttore di due argomenti.

.

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

Annotare il miglioramento rispetto if else non sono solo i 6 caratteri salvati. Con la sintassi di Scala IDE la colorazione delle parole chiave è la stessa (ad es. Viola) per if, else, e true, c'è un contrasto migliore in alcuni casi (che non viene mostrato dalla seguente colorazione della sintassi come attualmente visualizzato su questo sito):

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