[kotlin] 람다식에서 return을 사용해보자 !

고럭키·2021년 10월 5일
2

Kotlin

목록 보기
2/2

코틀린 기본기를 정리하는 목적으로 부스트코스 - 코틀린 프로그래밍 강의를 듣고 있다고 이전 포스팅에서 언급했었다 - 하루에 한 주제씩 15분정도만 투자해서 조금씩 꾸준히 계속 듣고있다 !

들으면서 굉장히 기본적인 내용이지만 제대로 모르고 있었던 내용이 있어서 정리해보고자 한다. 바로 람다식에서 return을 사용하는 방법이다 ~ (지금까지 람다식을 사용하면서 return을 사용한 적이 없어서 잘 모르고 있었던 것 같다..! 하지만 모르고 있어서 사용하지 않았던 것일 수 있다고 생각해서 알아둔다면 더 다양한 경우의 수를 고민할 수 있을 것 같다. )

람다식에서 return을 사용하려면 ?

람다식에서 return을 사용하려면 어떻게 해야할까 - 사실 람다식에서는 반환값이 필요할 경우 마지막 줄의 표현식을 반환하기 때문에 return을 사용할 일이 잘 없는 것 같다. 하지만 return이 흐름의 중단을 목적으로 사용되는 경우도 있기 때문에 이런 경우에는 어떻게 사용하면 되는지 예시를 통해서 알아보자 !

아래처럼 람다식을 파라미터로 가지는 함수를 선언하고, 이를 사용하는 부분에서 아래와 같이 람다식을 정의해주었다.

fun lambdaParam(a: Int, b: Int, lambda: (Int, Int) -> Unit){
	lambda(a, b)
}

fun func(){
	println("start of func")
	lambdaParam(13, 3) { a, b ->
		val result = a+b
		if(result > 10) return
		println("result: $result")
	}
	println("end of func") 
}

람다식에서 result의 값이 10보다 크다면 함수의 흐름을 중단하고 싶다. 그래서 람다식 내에서 return을 사용해주는 코드를 위와 같이 작성해보았다. 하지만 return 부분에서 오류가 발생하였다..!

일반적으로 람다식 내부에서는 return을 사용할 수 없다 !

하지만 위와 같이 람다식이 함수의 파라미터로 쓰이는 경우 아래의 코드처럼 해당 함수를 inline으로 선언하면 return을 사용할 수 있다.

inline fun lambdaParam(a: Int, b: Int, lambda: (Int, Int) -> Unit){
	lambda(a, b)
}

fun func(){
	println("start of func")
	lambdaParam(13, 3) { a, b ->
		val result = a+b
		if(result > 10) return
		println("result: $result")
	}
	println("end of func") 
}

이 코드에서 우리가 기대하는 결과는 result가 16으로 10보다 크기 때문에 result: 13은 출력되지 않고 아래와 같이 출력되는 것이다.

start of func
end of func 

하지만 실제로 위의 코드를 실행해보면 아래와 같이 end of func도 출력되지 않는 것을 확인할 수 있다.

start of func

그말인 즉슨 return을 통해서 해당 람다식만을 빠져나오는 것이 아닌 람다식을 매개변수로 가지는 inline함수를 호출하는 함수를 빠져나오게 된다. inline함수이기 때문에 이런 비지역 반환 현상이 발생한 것이다.

위에서도 강조했듯 일반적으로 람다식 내부에서는 return을 사용할 수 없으며, 사용하기 위해서는 inline function과 함께 사용해야 하는데 이러한 경우에는 의도한대로 코드가 흘러가게 하기 위해서는 위와 같은 사항을 주의해야 할 것 같다 !

Label이 뭐고, Label을 사용하면 ?

그렇다면 우리가 원하는대로 람다식만 빠져나오게 하기 위해서는 어떻게 해야할까 ! 바로 라벨이라는 것을 사용하면 된다.

Label은 특정 표현식에 별칭을 붙여주어, 흐름 제어 구문에서 점프 포인트를 지정하기 위한 목적으로 사용된다.

기본적으로 아래의 형식으로 사용할 수 있다. return 외에도 break, continue 등의 흐름 제어를 위한 구문에서 모두 사용할 수 있다.

람다식 함수명 라벨이름@ {
	return@라벨이름 
}

그럼 이 라벨을 사용해서 위의 예시로 살펴봤던 코드가 우리가 원하는대로 동작하도록 다시 코드를 작성해보자.

fun lambdaParam(a: Int, b: Int, lambda: (Int, Int) -> Unit){
	lambda(a, b)
}

fun func(){
	println("start of func")
	lambdaParam(13, 3) point@{ a, b ->
		val result = a+b
		if(result > 10) return@point
		println("result: $result")
	}
	println("end of func") 
}

람다식에 point라는 라벨을 달아주고 return문에 해당 라벨을 지정해줌으로 해당 람다식만 빠져나와서 end of func이 출력되는 결과를 얻을 수 있다.

람다식 내부에서 label을 이용하여 return해주는 것의 이해를 돕기 위해서 예시를 하나 더 추가하고 넘어간다. 아래처럼 람다식의 반환값을 통해서 특정 변수를 할당할 수 있다.

val isPositiveLabel : (Int) -> Boolean = number@{
    return@number it > 0
}

아닛.. 그럼 람다식에서 return을 사용하기 위해서는 이렇게 매번 라벨을 지정해줘야해..? ㅜㅡㅜ
그렇지 않다 !

암묵적 라벨 / 묵시적 라벨은 따로 라벨을 지정하지 않아도 함수의 이름을 라벨로 사용할 수 있는 것을 말한다.

암묵적 라벨을 사용하면 아래와 같이 위의 코드를 바꿀 수 있다.

fun lambdaParam(a: Int, b: Int, lambda: (Int, Int) -> Unit){
	lambda(a, b)
}

fun func(){
	println("start of func")
	lambdaParam(13, 3) { a, b ->
		val result = a+b
		if(result > 10) return@lambdaParam
		println("result: $result")
	}
	println("end of func") 
}

이 외에도 암묵적 라벨에 대한 이해를 위해 예시를 하나 더 첨부해보려 한다.

fun func() {
    nums.forEach(fun(value: Int) {
        if (value == 3) return@forEach
        print(value)
    })
    print("end of func")
}

람다식 말고, 익명 함수에서 return을 사용하면 ?

위처럼 라벨을 사용해서 우리가 원하는 흐름대로 동작하게 만들수도 있지만, 익명 함수를 사용해도 우리가 원하는 결과를 얻을 수 있다.

fun func(){
	println("start of func")
	lambdaParam(13, 3, fun(a, b){
		val result = a+b
		if(result > 10) return
		println("result: $result")
	}) 
	println("end of func") 
}

이처럼 람다식이 아닌 익명함수를 통해서 코드를 작성하면 비지역 반환이 발생하지 않고 아래와 같이 우리가 원하는 결과를 얻을 수 있다.

start of func
end of func 

람다식 내부에서는 return을 사용하지 못한다고만 알고있어서 여태 이런 내용을 찾아볼 일이 없었다. 사실 람다식 내부에서 return 사용할 일이 많지 않은 것 같지만 사용을 위해서 inline을 사용할 때 비지역 반환이 된다는 주의 사항은 모르던 내용이기에 적어두고자, 그리고 겸사겸사 label과 관련한 내용도 정리하고자 포스팅을 해보았다. 끄읕 !

1개의 댓글

좋은 글 감사합니다~ 도움 받고 갑니다 :)

답글 달기