2011-02-09 3 views
73

Я пытаюсь избегать конструкций, как это:троичного Оператор похожий на:

val result = this.getClass.getSimpleName 
if (result.endsWith("$")) result.init else result 

Хорошо, в этом примере then и else ветви просты, но вы можете изображения сложные. я построил следующее:

object TernaryOp { 
    class Ternary[T](t: T) { 
    def is[R](bte: BranchThenElse[T,R]) = if (bte.branch(t)) bte.then(t) else bte.elze(t) 
    } 
    class Branch[T](branch: T => Boolean) { 
    def ?[R] (then: T => R) = new BranchThen(branch,then) 
    } 
    class BranchThen[T,R](val branch: T => Boolean, val then: T => R) 
    class Elze[T,R](elze: T => R) { 
    def :: (bt: BranchThen[T,R]) = new BranchThenElse(bt.branch,bt.then,elze) 
    } 
    class BranchThenElse[T,R](val branch: T => Boolean, val then: T => R, val elze: T => R) 
    implicit def any2Ternary[T](t: T) = new Ternary(t) 
    implicit def fct2Branch[T](branch: T => Boolean) = new Branch(branch) 
    implicit def fct2Elze[T,R](elze: T => R) = new Elze(elze) 
} 

Определено, что я могу заменить выше простой пример с:

this.getClass.getSimpleName is {s: String => s.endsWith("$")} ? {s: String => s.init} :: {s: String => s} 

Но как я могу избавиться от s: String =>? Я хочу что-то вроде этого:

this.getClass.getSimpleName is {_.endsWith("$")} ? {_.init} :: {identity} 

Я думаю, что компилятору нужны дополнительные материалы для вывода типов.

+0

Поскольку я не на самом деле есть это в моем ответе - Причина, по которой» у вас возникли проблемы в том, что вывод типа работает лучше всего слева направо, но вы привязываете свои жетоны вместе справа налево из-за приоритета оператора. Если вы сделаете все ваши слова слов (с тем же приоритетом) и измените способ группировки, вы получите требуемый вывод. (Т.е. у вас будут классы «HasIs», «IsWithCondition», «ConditionAndTrueCase», которые будут создавать части выражения слева направо.) –

+0

Я бессознательно предполагал способ вывода типа слева направо, но придерживался приоритета оператора и ассоциативность имен методов, особенно начиная с '?' перед любым другим символом alphanum в качестве имени метода first char и ':' для левой ассоциативности. Поэтому я должен переосмыслить новые имена методов, чтобы получить вывод типа, действующий слева направо. благодаря! –

ответ

20

Мы можем объединить How to define a ternary operator in Scala which preserves leading tokens? с ответом на Is Option wrapping a value a good pattern?, чтобы получить

scala> "Hi".getClass.getSimpleName |> {x => x.endsWith("$") ? x.init | x} 
res0: String = String 

scala> List.getClass.getSimpleName |> {x => x.endsWith("$") ? x.init | x} 
res1: String = List 

Является ли это достаточным для ваших нужд?

+0

Это очень близко к тому, что я имею в виду. хороший подход. Я подумаю об этом. Моя причина избежать самого первого кода заключалась в том, чтобы быть более кратким в том, что у вас не было временного 'val' для следующего выражения' if': сделайте его понятным в одной строке, точно так же, как это имеет в виду. –

13

Rex Kerr’s answer выражается в основном Scala:

"Hi".getClass.getSimpleName match { 
    case x if x.endsWith("$") => x.init 
    case x => x 
} 

хотя я не уверен, какую часть, если-иначе построить вы хотите оптимизировать.

+0

очень прямой путь. иногда один забывает о ежедневных операциях совпадения/случая. Я просто придерживался однолинейной тройной «if then else» идиомы, но это действительно понятный способ решения. –

+0

Образец Соответствует весы легко для более чем двух ветвей. – Raphael

100

От Tony Morris' Lambda Blog:

Я слышу этот вопрос много. Да. Вместо c ? p : q написано if(c) p else q.

Возможно, это не является предпочтительным. Возможно, вы хотели бы написать его, используя тот же синтаксис, что и Java. К сожалению, вы не можете. Это связано с тем, что : не является допустимым идентификатором. Не бойтесь, | есть! Вы согласились бы на это?

c ? p | q 

Тогда вам понадобится следующий код. Обратите внимание на аннотации по именам (=>). Для правильной перезаписи Java-тернарного оператора требуется эта стратегия оценки. Это невозможно сделать в самой Java.

case class Bool(b: Boolean) { 
    def ?[X](t: => X) = new { 
    def |(f: => X) = if(b) t else f 
    } 
} 

object Bool { 
    implicit def BooleanBool(b: Boolean) = Bool(b) 
} 

Вот пример, используя новый оператор, который мы только что определили:

object T { val condition = true 

    import Bool._ 

    // yay! 
    val x = condition ? "yes" | "no" 
} 

Весело;)

+0

Да, я видел это раньше, но разница в том, что у меня есть (оцененное) значение моего первого выражения как аргумент в предложении 'then' и' else'. –

+0

@Imre: Я скорректировал ссылку и скопировал ее. – Landei

+2

Я взял подход 'if (c) p else q' ... отсутствие брекетов вызывает у меня дискомфорт, но это просто стиль – rjohnston

0

С: само по себе не будет являться действительным оператором, если вы не в порядке всегда избегая его обратных тиков :, вы можете пойти с другим персонажем, например "|" как в одном из ответов выше. Но как насчет Элвиса с бородкой? ::

implicit class Question[T](predicate: => Boolean) { 
    def ?(left: => T) = predicate -> left 
} 
implicit class Colon[R](right: => R) { 
    def ::[L <% R](pair: (Boolean, L)): R = if (q._1) q._2 else right 
} 
val x = (5 % 2 == 0) ? 5 :: 4.5 

Конечно, это опять-таки не будет работать, если вы значение списки, так как у них есть :: оператор сами.

Смежные вопросы