2010-04-24 4 views
8

Ich schreibe einen Code-Generator, der Scala-Ausgabe produziert.Wie definiert man einen ternären Operator in Scala, der führende Tokens erhält?

Ich muss einen ternären Operator so emulieren, dass die Token zu '?' intakt bleiben.

z.B. Konvertieren Sie den Ausdruck c ? p : q in c something. Das einfache if(c) p else q erfüllt meine Kriterien nicht, da es erforderlich ist if( vor c zu setzen.

Mein erster Versuch (noch mit c/p/q wie oben) wird

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

eine weitere Option, ich fand, war:

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

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

, die mir schreiben können:

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

Ich mag das allgemeine Aussehen der zweiten Option, aber gibt es eine Möglichkeit, es weniger wortreich zu machen?

Dank

+0

z etwas mit '_' anstelle von b: Boolean wäre nett, aber ich konnte nichts richtig arbeiten –

+1

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

Antwort

14

Auch wenn die Syntax nicht in der erwarteten Reihenfolge wertet - es bindet die bedingte an der ersten Option - Sie können Ihren eigenen ternären Operator wie diese machen können:

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) 

Der Trick besteht darin, ? als eine Methode auf einem MakeIfTrue-Objekt zu interpretieren, das die Bedingung an das Objekt bindet, um in dem "wahren" Fall zurückzukehren. Das resultierende IfTrue-Objekt verwendet jetzt die |-Methode als Anforderung zum Auswerten der Bedingung, wobei die gespeicherte True-Option zurückgegeben wird, wenn die Bedingung wahr ist, oder die gerade übergebene, wenn sie falsch ist.

Beachten Sie, dass ich Sachen wie => A anstelle von nur A - By-Name-Parameter verwendet habe, um den Ausdruck nicht auszuwerten, es sei denn, es wird tatsächlich verwendet. Daher werten Sie nur die Seite aus, die Sie tatsächlich benötigen (genau wie eine if-Anweisung).

Sagen wir es in Aktion sehen:

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 Um Verwirrung zu vermeiden, mit dem Schauspieler Bibliothek ?, sollten Sie wahrscheinlich es nennen etwas anderes wie |?.)

+0

Doh, zu langsam. Ich war gerade dabei, etwas Ähnliches zu tippen, nur um die gefürchtete Nachricht "2 neue Antworten" zu bekommen. –

+0

@Jackson: Interessante Alternativen sind immer noch lohnenswert! –

+0

Schön! Nur was ich ändern werde ist? zu | um die niedrigste Betreiberpriorität zu erhalten. –

4

Sie so etwas wie dies nutzen könnten

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" 

Dieser Code wandelt jede Boolesche Wert in einen anonymen Typ, das eine Methode aufgerufen hat ?. Abhängig vom Wert des booleschen Werts gibt diese Methode entweder TernarySecond oder TernaryThird zurück. Beide haben eine Methode namens >, die den zweiten Operanden bzw. den dritten Operanden zurückgibt.

14

Sagen wir es einfach halten:

Java:

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

Scala:

tmp = if (a > b) a else b 

Neben der Einfachheit ist es klar und schnell, weil: keine Objekte zuweisen Sie nicht brauchen , hält den Garbage-Collector aus der Gleichung (wie es immer sein sollte) und macht Prozessor-Caches besser nutzbar.

+1

Das ist meine Lieblingsantwort. Ihre Erklärung ist so einfach wie die Lösung. – theJollySin

+2

Dies löst ein anderes Problem, nicht das in der Frage angegebene Problem. –

0

Ternary Betreiber, die meine Verbesserung nach bestem Rex Kerr und Michel Krämer Implementierungen ergänzt:

  • Meine Verbesserung verwenden Scala neuen Wert Klasse-Box Overhead zu vermeiden.
  • Auf den zweiten und dritten Operanden namentlich anrufen, so dass nur der ausgewählte ausgewertet wird.
  • Michels Ruf by-Wert auf dem 1. (booleschen) Operanden, um Namen zu vermeiden Overhead; es wird immer ausgewertet.
  • Rex 'konkrete Klasse für die Bedingung, um anonyme Klassengemeinkosten zu vermeiden.
  • Michel's Bewertung der Bedingung zu bestimmen, welche Klasse zu konstruieren, um Overhead eines Konstruktors mit zwei Argumenten zu vermeiden.

.

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

Notiere die Verbesserung gegenüber if else ist nicht nur die 6 gespeicherten Zeichen. Da die Syntax der Scala IDEs für Schlüsselwörter die gleichen ist (zB violett) für if, else, null und true, gibt es in einigen Fällen einen besseren Kontrast (der nicht durch die Syntaxfarben unten auf dieser Site dargestellt wird):

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