스칼라에서 리프팅

Suraj P 2023년1월30일
  1. 메소드를 함수로 변환
  2. Scala에서 순수 함수를 펑터로 변환
  3. 스칼라에서 부분 함수를 함수로 변환
  4. 스칼라의 모나드 트랜스포머
  5. 결론
스칼라에서 리프팅

이 기사에서는 스칼라 프로그래밍 언어에서 리프팅에 대해 설명합니다. 리프팅에는 다른 의미가 있습니다. 그것은 우리가 그것을 사용하는 컨텍스트에 달려 있습니다.

하나씩 살펴보겠습니다.

메소드를 함수로 변환

우리는 때때로 메소드를 함수로 변환하려는 상황에 직면합니다. 이것은 리프팅의 한 예입니다.

예를 들어 다음과 같은 메서드가 있다고 가정해 보겠습니다.

def mul(x: Int) = x*5
def isEven(x: Int) = x%2==0

이제 이러한 메서드를 아래와 같이 구성할 수 있습니다.

isEven(mul(4))

그러나 이 구성이 기능적인 방식으로 발생하기를 원한다면 어떻게 될까요? 그런 다음 위의 메서드를 함수로 변환해야 합니다.

스칼라에서 _ 기호를 사용하여 들어올리면 됩니다.

val mulAns = mul _
val isEvenAns = isEven _

이제 기능적으로 다음과 같이 구성할 수 있습니다.

( mulAns andThen isEvenAns)(5)

전체 작업 코드:

object MyClass {
    def mul(x: Int) = x*5
    def isEven(x: Int) = x%2==0

    def main(args: Array[String])
    {
        println(isEven(mul(4)));

        val mulAns = mul _
        val isEvenAns = isEven _

        println(( mulAns andThen isEvenAns)(5))
    }
}

출력:

true
false

설명:

첫 번째 출력은 true입니다. 4에 5를 곱하면 20이 됩니다. 이 20은 isEven 함수에 매개변수로 전달됩니다.

두 번째 출력은 false입니다. 먼저 5에 5를 곱한 다음 isEven 함수에서 결과를 확인합니다.

Scala에서 순수 함수를 펑터로 변환

Functor는 개체 또는 개체의 기능과 유사하게 범주 간의 매핑을 나타냅니다. 예를 들어 AB 범주와 A 개체를 B 개체에 매핑하는 펑터 F가 있습니다.

trait Functor[F[_]] {
    def mapping[A, B](X: F[A])(f: A => B): F[B]

    def lifting[A, B](f: A => B): F[A] => F[B] =
        X => map(X)(f)
}

이 맥락에서 리프팅은 A=>B에서 함수를 가져와 F[A] => F[B] 형식의 함수로 만들어 펑터로 변환하는 것을 의미합니다. 중첩 데이터 유형으로 작업할 때 이는 매우 유용할 수 있습니다.

스칼라에서 부분 함수를 함수로 변환

이 컨텍스트에서 리프팅은 도메인의 확장을 나타냅니다. 부분 함수는 값의 하위 영역에 적용할 수 있는 함수의 종류입니다.

그러나 때때로 우리는 그들의 영역을 확장하고 싶을 수 있으며 리프팅은 우리가 그것을 달성하는 데 도움이 됩니다.

양수의 제곱근을 제공하는 부분 함수가 있다고 가정해 보겠습니다.

val sqrt: PartialFunction[Double, Double] =
{
    case temp if temp >= 0 => Math.sqrt(temp)
}

이제 lift 메서드를 사용하여 부분 함수의 영역을 확장하여 더 우아하게 보이게 할 수 있습니다.

def getSqrtRootPartialFunction(t: Double) =
{
    sqrt.lift(t).map(ans => s"Square root of ${t} is ${ans}")
        .getOrElse(s"Cannot calculate square root for t")
}

전체 작업 코드:

object MyClass {
     val sqrt: PartialFunction[Double, Double] = {
        case temp if temp >= 0 => Math.sqrt(temp)
    }

    def getSqrtRootPartialFunction(t: Double) = {
        sqrt.lift(t).map(ans => println(s"Square root of ${t} is ${ans}"))
           .getOrElse(println(s"Cannot calculate square root for t"))
    }

    def main(args: Array[String])
    {
        getSqrtRootPartialFunction(35)
    }
}

출력:

Square root of 35.0 is 5.916079783099616

설명:

그래서 여기에서 sqrt 기능의 해제는 PartialFunction[Double, Double]에서 Function[Double, Option[Double]]으로 도메인을 전체 이중으로 확장했다는 것입니다.

경우에 따라 index out of bound 예외를 피하기 위해 부분 함수를 들어올리는 것이 사용됩니다.

Seq("1", "2", "3").lift(1) // Some("2")

Seq("1", "2", "3").lift(7) // none returned

도메인이 String에서 Option[String]으로 확장되었습니다. 따라서 범위를 벗어난 값에 액세스하려고 하면 해제된 Seq범위를 벗어난 예외를 throw하는 대신 없음을 반환합니다.

스칼라의 모나드 트랜스포머

모나드를 함께 결합하는 것은 모나드 변환기를 사용하여 수행됩니다. 두 개의 Futures가 있다고 가정해 보겠습니다.

val helloWorld: Future[Option[String]] = Future.successful(Some("hello world"))
val language: Future[String] = Future.successful("this is Scala language")

이제 Futures는 서로 다른 도메인을 가지므로 languageOptionT로 올려서 모나드 변환기를 사용할 수 있습니다. 이를 통해 보다 일관된 방식으로 결과를 처리할 수 있습니다.

def MonadTransformer() = {
    val message: OptionT[Future, String] = for {
        hello <- OptionT(helloWorld)
        name  <- OptionT.liftF(language)
    } yield println(s"$hello $name")

    val result: Future[Option[String]] = message.value

    Await.result(result, 1 second)
}

결론

이 기사에서 우리는 리프팅의 개념과 다양한 상황에서 그것이 의미하는 바를 배웠습니다. 코드의 다른 부분을 구축하는 데 시간을 낭비하는 대신 코드의 비즈니스 로직에 더 집중할 수 있도록 보다 구성 가능하고 관용적인 방식으로 코드를 작성하는 것이 매우 유용합니다.

작가: Suraj P
Suraj P avatar Suraj P avatar

A technophile and a Big Data developer by passion. Loves developing advance C++ and Java applications in free time works as SME at Chegg where I help students with there doubts and assignments in the field of Computer Science.

LinkedIn GitHub

관련 문장 - Scala Function