[kotlin] 3. 함수형 프로그래밍

References: Do it! 코틀린 프로그래밍


함수 구조

1
2
3
4
5
6
package chap03.section1  

fun sum(a: Int, b: Int): Int { // fun 키워드 // 함수 이름 // 매개변수 // 반환자료형 명시
var sum = a + b // 함수 본문
return sum // 반환
}

한줄로 단축도 가능하다

1
fun sum(a: Int, b: Int): Int = a + b

코틀린 함수 특성

함수는 스택 메모리에 저장된다.
스택은 각 함수가 Frame단위로 적재된다.
LIFO인 스택 구조상 Main() 함수의 프레임이 가장 바닥에 적재되고, 그위에 max()함수 등 호출되는 순서대로 적재된다.

1
2
3
4
5
6
7
힙 영역 (낮은 주소)  

min()함수 Frame
max()함수 Frame
main()함수 Frame

스택 영역(높은 주소)

함수는 스택 영역의 높은 주소부터 거꾸로 자라듯이 채워져 나간다.
가장 나중에 호출된 min 함수가 가장 위로 올라가고, 호출이 종료되면 Frame단위로 사라진다.
함수 내부의 변수들은 서로 겹치지않으므로 함수 내부의 변수들끼리 계산이 가능하다.

Kotlin에서는 함수의 반환값을 생략할 수 있다.
단, 함수의 반환값을 생략하고 함수를 작성하면 Unit이라는 특수한 자료형으로 반환된다.

1
2
3
fun printSum(a: Int, b: Int): Unit { //Unit은 없어도 상관없음  
println("sum of $a and $b is ${a + b}")
}

함수 내에서 여러개의 매개변수를 받을 때, 가변 인자를 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
fun main() {  
normalVarargs(1,2,3,4)
normalVarargs(4,5,6)
}

fun normalVarargs(vararg counts: Int) { // 가변인자 vararg
for (num in counts) {
print("$num ")
}
print("\n")
}
1
2
3
실행결과
1 2 3 4
4 5 6

variable argument, 가변 인자의 약자 vararg를 사용하면 그때그때 변하는 인자의 개수를 전부 매개변수로 받아 사용 할 수 있다.

함수형 프로그래밍 구조

코틀린은 함수형 프로그래밍과 객체지향형 프로그래밍을 모두 지원하는 다중 패러다임 언어 이다.

  • 다중 패러다임 언어 : 한가지 구현 규칙에 얽매이지 않고 다양한 문법과 형식을 지원하는 언어

함수형 프로그래밍?

함수형 프로그래밍의 정의와 특징

  • 순수 함수를 사용해야 한다.
  • 람다식을 사용할 수 있다.
  • 고차 함수를 사용할 수 있다.
1. 순수함수

함수형 프로그래밍은 순수 함수를 작성하여 프로그램의 부작용을 줄이는 프로그래밍 기법을 말한다.

  • 순수 함수의 조건
    1. 같은 인자에 대하여 항상 같은 값을 반환하는 함수
    2. 함수 외부의 어떤 상태도 바꾸지 않는 함수
1
2
3
4
순수 함수의 예    
fun sum(a: Int, b: Int): Int {
return a + b
}
1
2
3
4
5
6
순수 함수를 만족하지 않는 함수의 예  

fun check() {
val test = User.grade()
if (test != null) process(test) // test는 User.grade()의 상태에 따라 달라짐
}
2. 람다식

람다식은 다음과 같은 형태로 이루어져있다.

{ x, y -> x + y }

함수의 이름이 없고, 화살표(->)가 사용되었다. 함수형 프로그래밍의 람다식은 다음과 같은것을 말한다.

  • 다른 함수의 인자로 넘기는 함수
  • 함수의 결과값으로 반환되는 함수
  • 변수에 저장하는 함수

람다식 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
변수에 할당하는 람다식  

//// 같은 람다식 ////
val multi: (Int, Int) -> Int = {x: Int, y: Int, Int -> x * y}
val multi = {x: Int, y: Int -> x * y}
val multi: (Int, Int) -> Int = {x, y -> x * y}
//// 같은 람다식 ////

val multi = {x,y -> x * y} // 이건 오류 (자료형이 없음)

val greet: () -> Unit = {println("Hello World!")} // 반환값이 없을때
val square: (Int) -> Int = {x -> x * x} // 매개변수가 하나일때

val nestedLambda: () -> () -> Unit = {{println("nested")}} // 람다식 안에 람다식

사용할경우 함수처럼 multi(x,y)로 사용하면 된다.

3. 일급 객체

함수형 프로그래밍에서는 함수를 “일급 객체” 로 생각한다. 일급 객체의 특징은 다음과 같다.

  • 일급 객체는 함수의 인자로 전달할 수 있다.
  • 일급 객체는 함수의 반환값에 사용할 수 있다.
  • 일급 객체는 변수에 담을 수 있다.

함수가 일급 객체면 일급 함수라고 부른다.
일급 함수에 이름이 없는 경우 람다식 함수 혹은 람다식이라고 부를 수 있다
즉 람다식은 일급 객체의 특징을 가진 이름 없는 함수이다.

4. 고차 함수

고차 함수 란 다른 함수를 인자로 사용하거나 함수를 결과값으로 반환하는 함수를 말한다.

인자가 일반 함수인 고차 함수 예시

1
2
3
4
5
6
7
8
9
10
11
인자가 일반 함수인 고차 함수  

fun main() {
val res1 = sum(3,2)
val res2 = mul(sum(3,3),3) // 인자에 함수 사용

println("res1: $res1, res2: $res2")
}

fun sum(a:Int, b:Int) = a + b
fun mul(a:Int, b:Int) = a * b
1
2
실행 결과  
res1: 5, res2: 18

반환값이 일반 함수인 고차 함수 예시

1
2
3
4
5
6
7
8
9
10
11
반환값이 일반 함수인 고차 함수  

fun main() {
println("funcFunc: ${funcFunc()}")
}

fun sum(a:Int, b:Int) = a + b

fun funcFunc():Int {
return sum(2,2) // 반환값이 함수
}
1
2
실행 결과  
funcFunc: 4

람다식을 매개변수에 할당하는 고차함수 예시

1
2
3
4
5
6
7
8
9
10
11
12
람다식을 매개변수에 할당하는 고차함수  

fun main() {
var result: Int
result = highOrder({ x, y -> x + y}, 10,20) // 람다식 함수 호출
println(result)
}

fun highOrder(sum: (Int, Int) -> Int, a: Int, b: Int): Int {
// 매개변수에 삽입된 람다식
return sum(a,b)
}
1
2
실행 결과  
30

인자와 반환값이 없는 람다식 고차함수 예시

1
2
3
4
5
6
7
8
9
인자와 반환값이 없는 람다식 고차함수

fun main() {
val out: () -> Unit = {println("Hello World")} // -> Unit 생략 가능

out() // 함수처럼 이용가능
val new = out // 다른 변수에 람다식 할당 가능
new()
}
1
2
3
실행 결과  
Hello World
Hello World
공유하기