SW 설계/스칼라

스칼라: 부분적용함수 (Partially Applied Function), 부분함수 (Partial Function) 그리고 Case

yztech 2021. 9. 25. 03:03
반응형

오늘은 소개할 내용은 부분적용함수, 부분함수, 그리고 case 문입니다.

 

스칼라에서 case=>처럼 다양한 용도로 사용됩니다.

스칼라에서 =>은 다양한 의미를 갖지만, 모두 수학적 의미가 있습니다.

스칼라에서 case도 다양한 의미로 사용되지만, 패턴 매칭의 의미가 있습니다.

 

partial function vs partially applied function

partial function과 partially applied function은 다릅니다.

partially applied function (부분 적용 함수)는 Don't Repeat Yourself (DRY)를 잘 따르는 코딩 방식중 하나로, 함수 및 코드 간략화를 위한 방법입니다.

함수의 일부인자가 고정된 값으로 주로 사용되는 경우, 그 인자를 계속넣을 필요가 없습니다.

이 때, val 사용하여 함수 객체를 지정하는데, 고정된 인자는 해당 값으로, 변하는 인자는 `_`으로 설정할 수 있습니다.

이는 함수 및 표현 간략화의 좋은 예이며, 기본값을 인자들을 가진 C/C++의 함수 포인터의 진보된 방식, 혹은 편리한 사용방식으로 생각할 수 있습니다.

def go(year: Int, where: String) = {
    println("let's go " + where " " + year)
}

def main(args: Array[String]): Unit = {
    go(2020,"Pusan")
    go(2020,"Seoul")

    val go2020 = go(2020, _: String)

    go2020("Pusan")
    go2020("Seoul")
}

 

=> 의 사용

scala에서 =>는 다양한 의미를 갖지만, 모두 수학적 의미가 있습니다.

타입 선언 시에는 L => R의 경우 L을 받아 R을 넘긴다는 의미로 사용됩니다.

  • A => T
    : val f: Int => String = n => "f:" + n 은 다음과 같습니다. val f: Function1[Int, String] = n => "f:" + n
  • (A,B) => T
  • () => T
  • () => Unit

함수 인자 선언 시에는 함수 자체를 넘긴다는 의미로 function arrow라고 부릅니다. 특히 lazy evaluation을 하여 인자이지만, 실제 사용 시 늦게 (lazy) 호출됩니다. 쉽게 c/c++에서 함수 포인터라고 생각하면 되고, call by value가 아닌 call by name으로 넘어간다고 보면 됩니다.

  • def f(param: => T):

중요한 것은 lazy evaluation이 필요할때 function arrow를 사용하면 됩니다.

 

case class

아래는 case class를 사용한 2개의 Person, Car 클래스들을 보여줍니다.

   abstract class Myobject
   case class Person(name: String, age: Int) extends Myobject
   case class Car(maker: String, number: String, year: Int) extends Myobject

케이스 클래스는 몇가지 특징이 있습니다.

  • 인스턴스를 만들기 쉽다: new 키워드가 필요없습니다.
 val john = Person("john", 15)

 val blackhorse = Car("kia", "12345678", 2021)
  • 기본적으로 불변: 필드를 직접 수정할 수 없습니다.
 john.age = 10 // compilation error
  • 파라미터들은 public이며, 인자는 멤버로 포함됩니다.
 val age = john.age
  • copy 메서드가 자동 생성되어, clone과 달리, 원하는 field만 변경한 사본을 만들 수 있습니다.
 val mike = john.copy(name="john")
  • equals method와 toString메서드가 자동 생성됩니다.
 val eagle = Car("kia", "12345678", 2021)

 if(blackhorse == eagle) {
   println("They are equal!")
 }

 println("blackhorse is " + blackhorse)

참고:

case class와 match 메서드를 사용한 패턴 매칭

case class는 match 메소드를 사용하면 패턴 매칭이 가능하여, 코드를 단순화시킬 수 있습니다. 이는 DRY를 잘 따르는 좋은 예중 하나입니다.

  • match와 함께 패턴 매칭을 사용할 수 있습니다.
 def show(obj: Myobject) : String = {
   obj match {
     case Person(name, _) => "His name is " + name
     case Car(maker, _, year) => "His car is " + maker + year
   }
 }

 println(show(john))
 println(show(blackhorse))

참고:

partial function과 case

부분 함수란 입력의 범위를 부분적으로 제한하는 함수를 말하고, case 문으로 구현합니다.

예를 들어, squareroot를 계산해서 반환하는 함수를 만들면, 입력값인 0 이상의 값으로 제한해야 합니다.

(1) squareRoot를 Double을 받아 Double을 반환하는 부분함 수로 만들기 위해 PartialFunction [Double, Double]로 선언합니다.

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

squareRoot를 PartialFunction으로 정의하면 자동으로 isDefinedAt 메서드가 생성된다. isDefinedAt함수는 그 함수의 입력을 미리 확인할 수 있습니다.

   println(squareRoot.isDefinedAt(4))  // true
   println(squareRoot.isDefinedAt(-4)) // false

참고:

 

반응형

'SW 설계 > 스칼라' 카테고리의 다른 글

스칼라: implicit 사용법 응용  (0) 2021.10.27
스칼라: implicit 기본 사용법  (0) 2021.10.27
스칼라: Mixins  (0) 2021.10.09
스칼라: Sealed, Final, Option, Try 클래스  (0) 2021.09.25