2010-04-24 13 views
8

Piszę generator kodu, który generuje wyjście Scala.Jak zdefiniować trójskładnikowego operatora w Scali, który zachowuje wiodące tokeny?

Muszę naśladować operatora trójskładnikowego w taki sposób, aby tokeny prowadzące do "?" pozostać nienaruszonym.

np. przekonwertuj wyrażenie c ? p : q na c something. Proste if(c) p else q zawiodło moje kryteria, ponieważ wymaga ono umieszczenia if( przed .

Moja pierwsza próba (wciąż przy C/P/q jak wyżej)

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

inną opcję znalazłem:

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

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

który pozwala mi napisać:

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

Podoba mi się ogólny wygląd drugiej opcji, ale czy jest jakiś sposób, aby ją mniej gadać?

Dzięki

+0

np. coś używającego "_" zamiast b: Boolean byłoby miłe, ale nie mogłem uzyskać niczego, aby zadziałało poprawnie –

+1

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

Odpowiedz

14

Chociaż składnia nie ocenia w oczekiwanej kolejności - wiąże warunkowego do pierwszej opcji - można zrobić własną operatora potrójny tak:

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) 

Sztuką jest interpretacja ? jako metody na obiekcie MakeIfTrue, która wiąże warunek z obiektem, aby powrócić w "prawdziwym" przypadku. Wynikowy obiekt IfTrue teraz używa metody | jako żądania do oceny warunku, zwracania przechowywanej prawdziwej opcji, jeśli warunek jest prawdziwy, lub just-passed-in, jeśli jest to fałsz.

Zauważ, że użyłem rzeczy takich jak => A zamiast tylko A - by-name parameters - aby nie oceniać wyrażenia, chyba że jest ono faktycznie używane. W ten sposób ocenisz tylko tę stronę, która faktycznie jest potrzebna (podobnie jak instrukcja if).

Zobaczmy, jak to działa:

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 Aby uniknąć pomyłek z biblioteki Aktor ?, pewnie powinienem nazwać to coś innego jak |?.)

+0

Doh, zbyt wolno. Byłem w trakcie pisania coś podobnego do tego, tylko po to, aby uzyskać przerażającą wiadomość "2 nowe odpowiedzi" –

+0

@ Jackson: Ciekawe alternatywy są nadal warte zachodu! –

+0

Nice! Zmieni się tylko jedna rzecz? do |? aby uzyskać najniższy priorytet operatora. –

4

Można użyć coś takiego

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" 

Kod ten przekształca dowolną wartość logiczną do anonimowego typu, który ma metodę zwaną ?. W zależności od wartości boolowskiej ta metoda zwróci wartość TernarySecond lub TernaryThird. Oba mają metodę o nazwie >, która zwraca odpowiednio drugi lub trzeci argument.

14

Trzymajmy go prosta:

Java:

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

Scala:

tmp = if (a > b) a else b 

Poza prostotą, jest oczywiste i szybko, ponieważ: nie przydzielić obiektów nie trzeba , utrzymuje garbage collector z równania (jak zawsze powinno być) i lepiej wykorzystuje pamięci podręczne procesora.

+1

To jest moja ulubiona odpowiedź. Twoje wyjaśnienie jest tak proste, jak rozwiązanie. – theJollySin

+2

Rozwiązuje to inny problem niż ten podany w pytaniu. –

0

Ternary operator, który dodaje się do poprawy mój najlepszy Rex Kerr i wdrożeń Michel Kramer:

  • Moja poprawa używać nowej klasy wartość SCALA, aby uniknąć narzutu bokserski.
  • Wywołanie nazwy-nazwy na 2. i 3. operandzie, aby ocenić tylko wybraną.
  • Wartość podrzędna wywołania Michela na pierwszym operandzie (boolowskim) w celu uniknięcia narzutu z nazwy użytkownika; jest zawsze oceniany.
  • Konkretna klasa Rexa dotycząca warunku uniknięcia jakiegokolwiek anonimowego obciążania klasy.
  • Ocena przez Michela warunku w celu określenia klasy do skonstruowania w celu uniknięcia narzutu konstruktora z dwoma argumentami.

.

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

Uwaga ulepszenie w stosunku do if else to nie tylko 6 znaków zapisanych. W przypadku kolorowania składni w programie Scala IDE w przypadku słów kluczowych, które są takie same (np. Fioletowy) w przypadku if, else, null i true, w niektórych przypadkach występuje lepszy kontrast (co nie jest uwidocznione w poniższej kolorystyce składni aktualnie renderowanej na tej stronie):

if (cond) true else null 
cond ? true | null 
Powiązane problemy