함수형 프로그래밍 이란?
한마디로 요약하면
부수 효과가 없는 순수 함수를 1급 객체로 간주하여
파라미터로 넘기거나, 반환값으로 사용할 수 있으며,
참조 투명성을 지킬 수 있다.
여기서 부수효과(Side Effect)란 다음과 같은 변화 또는 변화가 발생하는 작업을 의미한다.
- 변수의 값이 변경됨
- 자료 구조를 제자리에서 수정함
- 객체의 필드값을 설정함
- 예외나 오류가 발생하며 실행이 중단됨
- 콘솔 또는 파일 I/O가 발생함
그리고 1급 객체란 다음과 같은 것들이 가능한 객체를 의미한다.
- 변수나 데이터 구조 안에 담을 수 있다.
- 파라미터로 전달 할 수 있다.
- 반환값으로 사용할 수 있다.
- 할당에 사용된 이름과 무관하게 고유한 구별이 가능하다.
- Java는 완벽한 객체지향 프로그래밍 언어이다.
- (엄밀히 말하면 java 8 이후 함수형 프로그래밍, 람다, Stream Api 가 추가됨)
- 따라서 모든 코드는 클래스를 설계하고 메서드를 만들어주고 클래스를 통해 객체를 생성해서 사용해야 한다.
- 반면, Kotlin은 함수만 만들어 사용하는 것(함수형 프로그래밍)을 지원한다.
- 코틀린은 함수 사용을 보다 편리하게 할 수 있도록 다양한 개념들이 제공된다.
fun main() {
val r1 = testFun1(100, 200)
println("r1 : $r1")
val r2 = testFun2(100, 200)
println("r2 : $r2")
val r3 = testFun3(100, 200)
println("r3 : $r3")
val lambda1 : (Int, Int) -> Int = {a1:Int, a2:Int -> a1 + a2}
val lambda2 = {a1:Int, a2:Int -> a1 + a2}
val lambda3 : (Int, Int) -> Int = {a1, a2 -> a1 + a2}
val r4 = lambda1(100, 200)
println("r4 : $r4")
val r5 = lambda2(100, 200)
println("r5 : $r5")
val r6 = lambda3(100, 200)
println("r6 : $r6")
val r7 = testFun4(100, 200)
println("r7 : $r7")
val lambda4 = {a1:Int, a2:Int ->
val r1 = a1 + a2
val r2 = a1 - a2
r1 * r2
}
val r8 = lambda4(100, 200)
println("r8 : $r8")
}
fun testFun1(a1:Int, a2:Int) : Int {
return a1 + a2
}
fun testFun2(a1:Int, a2:Int) : Int = a1 + a2
fun testFun3(a1:Int, a2:Int) = a1 + a2
fun testFun4(a1:Int, a2:Int) : Int {
val r1 = a1 + a2
val r2 = a1 - a2
val r3 = r1 * r2
return r3
}
익명함수
- 함수의 이름이 없는 함수이다.
- 함수를 변수에 담아 관리할 때 사용한다.
- 고차함수와 관련이 깊다.
fun main() {
testFunction1()
// val testFunction2 = testFunction1
val testFunction2 = fun(){
println("testFunction2 입니다")
}
testFunction2()
}
fun testFunction1() {
println("testFunction1 입니다")
}
고차함수
- 함수를 매개변수로 받거나, 반환 타입이 함수인 함수를 고차 함수라고 부른다.
- 함수 호출 시 전달하는 함수와 반환 하는 함수는 람다식을 사용할 수도 있다.
1) 고차함수의 입력값으로 "익명함수"를 전달할 수 있다.
익명함수를 t1 에 저장하고, 그 값을 입력으로 하는 고차함수 testFunc1을 호출한다.
fun main() {
val t1 = fun(x1:Int, x2:Int) : Int {
return x1 + x2
}
testFunc1(t1, 100, 200)
}
fun testFunc1(m1:(Int, Int) -> Int, a1:Int, a2:Int){
val r1 = m1(a1, a2)
println("r1 : $r1")
}
근데 익명함수니까 아래 코드처럼.... 바로 익명함수를 고차함수의 입력 파라미터에 구현해도 된다.
fun main() {
testFunc1(fun(x1:Int, x2:Int) : Int {
return x1 + x2
}, 100, 200)
}
2) 고차함수의 입력값으로 "람다"식도 전달할수 있다.
람다식을 t1에 저장하고, testFunc1 고차함수의 입력 파라미터로 전달한다.
fun main() {
val t1 = {x1:Int, x2:Int -> x1 + x2}
testFunc1(t1, 100, 200)
}
람다도 아래 코드처럼 .... 바로 람다를 고차함수의 입력 파라미터에 구현해도 된다.
fun main() {
testFunc1({x1:Int, x2:Int -> x1 + x2}, 200, 100)
}
3) 고차함수의 반환값을 "익명함수"로 정의할 수 있다.
고차함수 testFunc2의 입력 파라미터는 없고,
출력 파라미터를 익명함수로 바로 정의했다.
t2 에는 리턴값으로 정의한 익명함수가 전달되게되고,
고차함수의 반환 값인.. t2 익명함수의 입력값을 100, 200 으로 준 함수를 r2라고 정의했다.
fun main() {
val t2 = testFunc2()
val r2 = t2(100, 200)
println("r2 : $r2")
}
fun testFunc2() : (Int, Int) -> Int {
return fun(x1:Int, x2:Int) : Int {
return x1 + x2
}
}
4) 고차함수의 반환값을 "람다"로 정의할 수 있다.
고차함수 testFunc2의 입력파라미터는 없고,
출력 파라미터로 람다식을 저의해주었다.
t2에는 고차함수 testFunc2의 반환값으로 정의된 람다식이 저장되고,
고차함수 반환값인 ...t2 람다식의 입력값으로 100과 200을 준 익명함수를 r2라고 하였다.
fun main() {
val t2 = testFunc2()
val r2 = t2(100, 200)
println("r2 : $r2")
}
fun testFunc2() : (Int, Int) -> Int {
return {x1:Int, x2:Int -> x1 - x2}
}
5) 연습 - 고차함수를 사용하면 편한 예제 : it, Unit 등 사용
* 고차함수 매개변수의 람다식의 입력 매개변수가 한개 일 때,
입력 매개변수를 생략하고, 출력 매개변수를 it으로 표현
어떤 값이 입력으로 들어오면 그 입력값에 100을 더해서 출력하는 람다식을 만들었다.
fun main() {
testFunc4({x1:Int -> x1 + 100}, 200)
}
fun testFunc4(m1:(Int) -> Int, a1:Int){
val r4 = m1(a1)
println("r4 : $r4")
}
이때 입력 매개변수인 람다식의 입력값이 하나이면 그 입력값을 it으로 정의하여,
입력값을 생략하고, 출력값만 표현하여 람다식을 만들 수 있다.
fun main() {
testFunc4({it + 100}, 200)
}
fun testFunc4(m1:(Int) -> Int, a1:Int){
val r4 = m1(a1)
println("r4 : $r4")
}
* 고차함수 매개변수의 함수형 입력 매개변수를 맨뒤에 구현할 때,
고차함수 입력 중괄호 닫고, 뒤에 추가 함수로 표현 가능하다.
함수를 받는 매개변수를 맨 뒤로 구현했을 경우는 다음과 같다.
fun main() {
testFunc5(100, 200, {x1:Int, x2:Int -> x1 + x2})
}
fun testFunc5(a1:Int, a2:Int, m1:(Int, Int) -> Int){
val r5 = m1(a1, a2)
println("r5 : $r5")
}
이때 마지막 람다식으로 들어갈 함수형 매개변수를
중괄호 닫고, 뒤에 추가로 구현해주어도 된다.
이 기능을 지원하는 이유는
만약 입력 람다식이 매우 길 경우
다음 줄로 내려서 구현하도록 하는것을 지원하기 위해서 이다.
fun main() {
testFunc5(100, 200) {x1:Int, x2:Int ->
x1 + x2
}
}
fun testFunc5(a1:Int, a2:Int, m1:(Int, Int) -> Int){
val r5 = m1(a1, a2)
println("r5 : $r5")
}
*고차함수 매개변수의 함수형 매개변수의 반환값이 없는 경우,
Unit으로 표현 가능하다.
fun main() {
testFunc6({x1:Int -> println(x1)})
}
fun testFunc6(m1:(Int) -> Unit){
m1(100)
}
앞에서 설명한것과 더해서 표현하면
매개변수가 하나밖에 없으니까 입력 매개변수 생략하고, 출력 매개변수를 it으로도 표현해보면 다음과 같다.
fun main() {
testFunc6{println(it)}
}
fun testFunc6(m1:(Int) -> Unit){
m1(100)
}
'Language > Kotlin' 카테고리의 다른 글
Kotlin 배열(Array), 문자열(String) (0) | 2021.04.06 |
---|---|
[Kotlin 함수형 (2)] 연산자 오버로딩 - inline(인라인), infix(중위) (0) | 2021.04.06 |
[Kotlin 객체지향 (1)] 클래스, 패키지, 모듈, 접근 제한자, 상속 (0) | 2021.04.06 |
Kotlin 자료형과 흐름제어 (0) | 2021.04.06 |
Kotlin 목차 (0) | 2021.04.06 |