복붙노트

[SCALA] 스칼라 등 구현 ifTrue, ifFalse, ifSome, ifNone은 피하는 경우 (...) 단순 패턴 매칭

SCALA

스칼라 등 구현 ifTrue, ifFalse, ifSome, ifNone은 피하는 경우 (...) 단순 패턴 매칭

스칼라, 나는 점진적으로 제어 흐름 지향적 인 방법으로 사고의 내 자바 / C 습관을 잃었으며, 가서 내가 먼저 관심 객체를 얻기 위해 사용되었다, 그리고 일반적으로 일치 또는지도 같은 것을 적용 () 또는 foreach는 () 모음. 지금 구조 내 코드의 좀 더 자연스럽고 더 투 포인트 방식 같은 느낌 때문에 나는 그것을 많이 좋아한다.

조금씩, 나는 내가 조건 같은 방법으로 프로그램 할 수달라고했습니다; 즉, 제 부울 값을 획득하고이를 다양한 일들을 일치. 본격적인 경기는, 그러나,이 작업에 대한 약간의 과잉을 보이지 않습니다.

비교:

obj.isSomethingValid match {
  case true => doX
  case false => doY
}

내가 가까이 자바 스타일로 쓸 것입니다 무슨 대 :

if (obj.isSomethingValid)
  doX
else
  doY

메시지 (및 그 변형) 및 ifFalse : 그럼 난 스몰 토크의 ifTrue를 기억해 냈습니다. 스칼라에서이 같은 뭔가를 쓸 수 있을까?

obj.isSomethingValid ifTrue doX else doY

변종 :

val v = obj.isSomethingValid ifTrue someVal else someOtherVal

// with side effects
obj.isSomethingValid ifFalse {
  numInvalid += 1
  println("not valid")
}

또한,이 스타일은 옵션 같은 간단한 두 가지 상태 유형에 사용할 수 있을까? 내가 사용 옵션에 더 관용적 방법, 수집 및 전화 필터 (),지도 ()로 처리하는 것입니다 그것을) (존재 알고 있지만, 종종 말에, 나는 몇 가지 DOX이 경우를 수행 할 것을 발견 그렇지 않은 경우 정의, 일부 도이된다. 뭔가 같은 :

val ok = resultOpt ifSome { result =>
  println("Obtained: " + result)
  updateUIWith(result) // returns Boolean
} else {
  numInvalid += 1
  println("missing end result")
  false
}

나를 위해,이 (아직?) 본격적인 경기보다 더 보인다.

내가 생각 해낸 기본 구현을 제공하고; 이 스타일 / 기술 및 / 또는 더 나은 구현에 대한 일반적인 의견을 환영합니다!

해결법

  1. ==============================

    1.첫째 :이 키워드이며, 역 따옴표를 사용하여 식별자 오히려 못생긴 나는 대신 다른 사용합니다 그래서, 볼 수 강제로 우리는 아마,하지 재사용 다른 수 있습니다.

    첫째 :이 키워드이며, 역 따옴표를 사용하여 식별자 오히려 못생긴 나는 대신 다른 사용합니다 그래서, 볼 수 강제로 우리는 아마,하지 재사용 다른 수 있습니다.

    여기에 구현하려는 시도이다. 먼저, 부울에 ifTrue 및 ifFalse을 추가 포주 - 내 - 라이브러리 패턴을 사용합니다. 그들은 지정된 조건이 실현되는 경우 평가해야 하나의 별 이름 매개 변수를 반환 타입 R에 매개 변수화하고 동의합니다. 그러나 그렇게함으로써, 우리는 다른 통화에 대해 허용해야합니다. 우리는 옵션 저장한다 [R]와 같은 수의 중간 결과를 Otherwise0 (0 설명한 이유 이상)이라는 새로운 객체를 반환 할 수 있도록. 그것은 현재의 조건 (ifTrue 또는 ifFalse)이 실현되는 경우 정의하고, 그렇지 않으면 빈입니다.

    class BooleanWrapper(b: Boolean) {
      def ifTrue[R](f: => R) = new Otherwise0[R](if (b) Some(f) else None)
      def ifFalse[R](f: => R) = new Otherwise0[R](if (b) None else Some(f))
    }
    implicit def extendBoolean(b: Boolean): BooleanWrapper = new BooleanWrapper(b)
    

    지금,이 작품은 나에게 쓰기를 할 수 있습니다

    someTest ifTrue {
      println("OK")
    }
    

    그러나, 그렇지 않다면 다음 절없이, 그것은 물론, 형 R의 값을 반환 할 수 없습니다. 그래서 여기 Otherwise0의 정의는 다음과 같습니다

    class Otherwise0[R](intermediateResult: Option[R]) {
      def otherwise[S >: R](f: => S) = intermediateResult.getOrElse(f)
      def apply[S >: R](f: => S) = otherwise(f)
    }
    

    단지는 이전 ifTrue 또는 ifFalse에서 가져온 중간 결과가 정의되지 않은 경우 경우는 원 정확히 인의 통과라는 이름의 인수를 평가합니다. 타입 파라미터 화 [S> R은] S는, 예를 들면, 위 코드에서, R은 과일 추론 타입을 가지도록 지정된 변수의 실제 유형의 최의 일반적인 수퍼 될 것으로 추정되는 효과가있다 :

    class Fruit
    class Apple extends Fruit
    class Orange extends Fruit
    
    val r = someTest ifTrue {
      new Apple
    } otherwise {
      new Orange
    }
    

    는 별명도 당신이 코드의 짧은 청크 전부 다른 메소드 이름을 건너 뛸 수 있습니다) (적용 :

    someTest.ifTrue(10).otherwise(3)
    // equivalently:
    someTest.ifTrue(10)(3)
    

    마지막으로, 여기에 옵션에 해당하는 포주는 다음과 같습니다

    class OptionExt[A](option: Option[A]) {
      def ifNone[R](f: => R) = new Otherwise1(option match {
        case None => Some(f)
        case Some(_) => None
      }, option.get)
      def ifSome[R](f: A => R) = new Otherwise0(option match {
        case Some(value) => Some(f(value))
        case None => None
      })
    }
    
    implicit def extendOption[A](opt: Option[A]): OptionExt[A] = new OptionExt[A](opt)
    
    class Otherwise1[R, A1](intermediateResult: Option[R], arg1: => A1) {
      def otherwise[S >: R](f: A1 => S) = intermediateResult.getOrElse(f(arg1))
      def apply[S >: R](f: A1 => S) = otherwise(f)
    }
    

    주 우리는 이제 우리가 편리하게 ifSome 함수 인수뿐만 아니라, 그렇지 않으면 ifNone 다음의 함수 인수에뿐만 아니라 미개봉 값을 통과 할 수 있도록하는 것이 Otherwise1이 필요하다는 것을.

  2. ==============================

    2.너무 구체적으로 문제를보고 할 수있다. 당신은 아마 파이프 연산자 더 나을 것입니다 :

    너무 구체적으로 문제를보고 할 수있다. 당신은 아마 파이프 연산자 더 나을 것입니다 :

    class Piping[A](a: A) { def |>[B](f: A => B) = f(a) }
    implicit def pipe_everything[A](a: A) = new Piping(a)
    

    지금 당신은 할 수

    ("fish".length > 5) |> (if (_) println("Hi") else println("Ho"))
    

    틀림없이, 당신이 달성하려고하는 것과 아주 우아하지 않다, 그러나 놀랍게도 다양한 있다는 큰 장점을 가지고있는 - 당신이 첫 번째 (단지 논리 값 포함) 인수를 넣어 원하는 시간, 당신은 그것을 사용할 수 있습니다 .

    또한, 당신은 이미 옵션 당신이 원하는 방법을 사용할 수 있습니다 :

    Option("fish").filter(_.length > 5).
      map (_ => println("Hi")).
      getOrElse(println("Ho"))
    

    이 것이 의미하는 것은 아니다 반환 값을 취할 수해서 당신이 그들을 피할 수 있습니다. 이 구문에 익숙해 조금 가지고 간다; 이것은 자신의 implicits을 만들 수있는 유효한 이유가 될 수 있습니다. 그러나 핵심 기능이있다. (당신이 당신의 자신을 만드는 경우에, 고려 배 [B] (F : A => B) (g :. => B) 대신에, 당신이 그것에 익숙해 일단 개입 키워드의 부족이 실제로 오히려 좋은)

    편집 :이 있지만 | 파이프 표기법 다소 표준> I 실제로 메소드 이름으로 사용하기를 선호하기 때문에 다음 DEF 재사용 [B, C] (F : A => B) (g (A, B) => C ) = g (a, F (a))은보다 자연스럽게 보인다.

  3. ==============================

    3.이유는 단지 다음과 같이 사용하지 마십시오 :

    이유는 단지 다음과 같이 사용하지 마십시오 :

    val idiomaticVariable = if (condition) { 
        firstExpression
      } else { 
        secondExpression 
      } 
    

    ?

    IMO, 매우 관용적! :)

  4. from https://stackoverflow.com/questions/5654004/implementing-iftrue-iffalse-ifsome-ifnone-etc-in-scala-to-avoid-if-and by cc-by-sa and MIT license