다음과 유사한 삼항 연산자? :


94

다음과 같은 구문을 피하려고합니다.

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

좋습니다.이 예에서 thenelse분기는 단순하지만 복잡한 것을 이미지화 할 수 있습니다. 다음을 구축했습니다.

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}

컴파일러는 유형을 추론하기 위해 추가 항목이 필요하다고 생각합니다.


실제로 내 대답에 이것을 가지고 있지 않았기 때문에 문제가있는 이유는 유형 추론이 왼쪽에서 오른쪽으로 가장 잘 작동하지만 연산자 우선 순위 때문에 토큰을 오른쪽에서 왼쪽으로 묶기 때문입니다. 모든 진술을 단어 (동일한 우선 순위로)로 만들고 사물이 함께 그룹화되는 방식을 변경하면 원하는 추론을 얻을 수 있습니다. (즉 HasIs, 왼쪽에서 오른쪽으로 표현식의 일부를 구성하는 IsWithCondition,, ConditionAndTrueCase클래스가 있습니다.)
Rex Kerr

나는 무의식적으로 왼쪽에서 오른쪽으로 유형 추론의 방법을 추측했지만 연산자 우선 순위와 메서드 이름의 연관성에 고착했습니다. 특히 ?다른 영숫자 문자보다 먼저 메서드 이름 첫 번째 문자와 :왼쪽 연관성으로 시작합니다. 따라서 유형 추론이 왼쪽에서 오른쪽으로 작동하도록하려면 새 메서드 이름을 다시 생각해야합니다. 감사!
Peter Schmitz

답변:


28

스칼라에서 선두 토큰을 보존하는 삼항 연산자를 정의하는 방법을 결합 할 수 있습니다 . 에 대한 대답으로 값이 좋은 패턴을 배치됩니다 옵션?얻기 위해

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

이것이 귀하의 필요에 적합합니까?


그것은 내가 생각하는 것과 매우 가깝습니다. 좋은 접근입니다. 그것에 대해 생각할 것입니다. 첫 번째 코드를 피하는 이유 val는 다음 if문에 대한 임시 코드가 없다는 점에서 더 간결 해지기 위해서였습니다.
Peter Schmitz

125

에서 토니 모리스 '람다 블로그 :

이 질문을 많이 듣습니다. 네 그렇습니다. 대신에 c ? p : q기록 if(c) p else q됩니다.

이것은 바람직하지 않을 수 있습니다. 아마도 Java와 동일한 구문을 사용하여 작성하고 싶을 것입니다. 슬프게도 할 수 없습니다. 이것은 :유효한 식별자 가 아니기 때문 입니다. 두려워하지 마십시오 |! 이것에 만족 하시겠습니까?

c ? p | q

그런 다음 다음 코드가 필요합니다. =>인수에 대한 call-by-name ( ) 주석을 확인하십시오. 이 평가 전략은 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) 
}

다음은 방금 정의한 new 연산자를 사용한 예입니다.

object T {   val condition = true

  import Bool._

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

재미있게 보내세요;)


예, 전에 이것을 보았지만 차이점은 I가 thenand else절의 인수로 내 첫 번째 표현의 (평가 된) 값을 가지고 있다는 것 입니다.
Peter Schmitz

5
나는했다 if(c) p else q중괄호의 부족이 나를 불편하게 터치한다하지만 그건 단지 스타일 일이 ... 접근을
rjohnston

17

기본 Scala로 표현 된 Rex Kerr의 답변 :

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

if-else 구조의 어떤 부분을 최적화하고 싶은지 잘 모르겠지만.


아주 똑바로. 때로 매일 사용하는 match / case 문을 잊어 버립니다. 나는 단지 한 줄의 삼항 if then else관용구를 고수했지만 실제로는 이해하기 쉬운 방법입니다.
Peter Schmitz

1
패턴 일치는 두 개 이상의 분기로 쉽게 확장됩니다.
Raphael


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

물론 값이 목록이면 :: 연산자가 있기 때문에 다시 작동하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.