내포 메서드

Volc·2023년 4월 5일
0

Scala

목록 보기
5/9

내포된 메서드의 예를 보자.

def factorial(i: Int): Long = {
	def fact(i: Int, accumulator: Long): Long = {
    	if (i <= 1) accumulator
        else fact(i - 1, i * accumulator)
    }
    fact(i, 1)
}

(0 to 5) foreach ( i => println(factorial(i)) )
  • fact() 함수가 내포된 메서드이다.
  • 길이가 긴 메서드의 본문을 여러 작은 메서드로 리팩토링하는 경우 유용하다.
  • fact()와 factorial에서 같은 이름의 i라는 매개변수를 사용하지만 factorial의 i는 가려진다.
  • 오직 fact()를 호출할 때만 사용된다.
  • fact()가 재귀적이고, 스칼라의 지역 영역 타입 추론은 재귀 함수의 반환 타입을 추론할 수 없기 때문에 fact()의 반환 타입을 꼭 명시해야 한다.

재귀 함수에 대한 의문

  • 재귀 함수의 경우 stack이 쌓여 메모리가 터질 수 있기 때문에 걱정이 된다.
  • scala의 경우 꼬리 재귀 최적화의 경우 while문 처럼 컴파일이 되기 때문에 괜찮다.

꼬리 재귀

  • 꼬리 재귀를 제대로 작성했는지는 @tailrec을 사용하면 잘 알 수 있다.
import scala.annotation.tailrec

def factorial(i: Int): Long = {
	@tailrec
    def fact(i: Int, accumulator: Int): Long = {
    	if (i <= 1) accumulator
        else fact(i-1, i * accumulator)
    }
    fact(i,1)
}

(0 to 5) foreach ( i => println(factorial(i)) )
  • 만약 fact가 꼬리 재귀가 아니라면, 컴파일러가 오류를 발생시킬 것이다.
  • 꼬리 재귀 함수는 마지막에 fact(i,1)처럼 내부 함수 하나만 사용하여 반환을 해야하며 사칙연산이 들어가면 안된다.

꼬리 재귀는 왜 사용할까?

  • 재귀 함수를 사용하면 좀 더 코드가 간결해지는데 꼬리 재귀로 만들면 최적화가 되어 메모리가 터지지 않으니까 사용하는게 아닐까?
  • 코드가 간결은 해지지만 꼬리 재귀로 짜기에는 머리가 좀 아플거 같다.
profile
미래를 생각하는 개발자

0개의 댓글