728x90
함수형 프로그래밍
- 함수형 프로그래밍은 수학의 함수적 개념을 참고해 만든 패러다임의 하나로, 깔끔하고 유지보수가 용이한 소프트웨어를 만들기 위해 함수를 사용한다.
- 함수형 패러다임은 부수효과가 없고, 똑같은 input이 들어오면 항상 같은 output을 내놓은 순수 함수의 개념을 기반으로 람다, 고차 함수, 커리, 메모이제이션, 모나드 등의 개념을 포함한다.
함수를 값으로 사용하기
fun main() {
/*
//코틀린에서 제공하는 함수형 프로그래밍
val list = mutableListOf(printHello) //리스트에 값으로 함수를 넣을 수 있다.
//list[0]() //함수를 실행할 때는 함수 실행 표기법인 ()를 반드시 써야한다
val func: () -> Unit = list[0]
func()
*/
//call(printHello)
//컴파일 오류: fun으로 선언한 객체는 일급 객체의 특성을 가지지 않기 때문에 데이터 구조에 넣거나, 함수 인자로 전달할 수 없다
/*
val func = printNo
val list = mutableListOf(printNo)
call(printNo)
*/
/*
val result = plus(1, 3, 5)
println(result)
*/
val list = listOf("a", "b", "c")
val printStr: (String) -> Unit = { println(it) }
//forEachStr(list, printStr)
list.forEach(printStr) //list에서 제공하는 forEach도 고차함수이다.
val upperCase: (String) -> String = {
it.uppercase()
}
println(list.map(upperCase))
}
val func: () -> Unit = {} //함수도 타입이라 이와 변수를 선언하듯이 선언 가능하다
//코틀린에서 함수는 일급 객체로, 변수에 대입할 수 있고, 데이터 구조에 저장할 수 있다.
val printHello: () -> Unit = { println("Hello") }
//함수를 인자로 받아 실행하는 함수
fun call(block: () -> Unit) {
block() //call 함수 내부에서, 인자로 받은 함수를 실행할 수 있다.
}
fun printNo() = println("No!")
val printMessage: (String) -> Unit = { message: String -> println(message) }
//val printMessage2: (String) -> Unit = { message -> println(message) } //message 타입 추론 가능,
//val printMessage3: (String) -> Unit = { println(it) } //message 참조를 it와 같은 내부 참조로 변경 가능
//다수의 인자를 받아 처리하는 함수
//val plus: (Int, Int) -> Int = { a, b -> a + b }
val plus: (Int, Int, Int) -> Int = { a, b, c -> a + b + c }
fun forEachStr(collection: Collection<String>, action: (String) -> Unit) {
//함수를 인자로 받아 내부적으로 함수를 실행하는것을 고차함수라고 한다
for (item in collection) {
action(item)
//println(item) //현재는 이 동작과 동일하다
}
}
- 함수형 프로그래밍에서, 함수는 1급 객체로 분류
- 1급 객체 : 함수를 인자에 넘기거나, 변수에 대입하거나, 함수를 반환하는 개념
- 함수도 타입이라 변수를 선언하듯이 사용이 가능하며, 함수는 값이 될 수도, 값은 함수가 될 수도 있으므로, 함수에 인자로 넘기거나 데이터 구조에 저장할 수 있다.
- 리스트에 저장해서 사용 가능하다.
- 변수로 받아 사용할 때는 반드시 ()를 사용해서, 함수를 실행시켜 주어야 한다.
- fun으로 선언된 함수는 값으로 다룰 수 없으며, 이를 변수에 대입하거나, 값으로 다룰 수 없다.
- 다수의 인자를 받아 처리하는 함수도 정의할 수 있다.
- 고차 함수는 함수를 인자로 받거나 결과로 돌려주는 함수를 의미한다.
- filter, map, foreach 함수 같이, 이미 여러 곳에서 많이 사용되고 있는 함수들은 모두 고차 함수이다.
익명 함수와 람다
package advanced
fun main() {
/*
outerFunc()() //익명 함수를 실행하기 위해서는 함수 표현식을 2번 써야한다.
val func = outerFunc()
func()
*/
//후행 람다 전달 : 함수 전달 인자가 하나이거나, 파라미터가 가장 뒤에 있는경우, 함수를 호출하면서, 람다식을 구현체로 전달 할 수 있다.
val list = listOf("a", "b", "c")
forEachStr(list) {
println(it)
}
arg1 {
it.length
it.first()
}
arg2 { a: String, b: String ->
//it 키워드 사용 안된다
a.length
b.first()
}
}
fun forEachStr(collection: Collection<String>, action: (String) -> Unit) {
//함수를 인자로 받아 내부적으로 함수를 실행하는것을 고차함수라고 한다
for (item in collection) {
action(item)
//println(item) //현재는 이 동작과 동일하다
}
}
/*
fun outerFunc(): () -> Unit {
return fun() { //이름 없는 익명 함수를 반환했다
println("이것은 익명함수!")
}
}
*/
/*
fun outerFunc(): () -> Unit {
return { //람다식 변경
println("이것은 익명함수!")
}
}
*/
fun outerFunc(): () -> Unit = { println("이것은 익명함수!") } //람다 함수를 생략해서 구현
//람다 표현식의 전체 식
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
//최대한 생략한 람다 식
val sum2 = { x: Int, y: Int -> x + y } //가독성을 판별해서, 적절하게 선택해서 사용하자
fun arg1(block: (String) -> Unit) {}
fun arg2(block: (String, String) -> Unit) {}
- 함수의 이름이 없는 함수를 익명 함수라고 한다.
- 익명 함수를 람다식으로 변경하면, 좀 더 가독성 좋고, 코드가 줄어든다
- 단, 람다식은 많은 것을 추상화하기 때문에, 개발자가 선택해서, 가독성이 좋게 작성하는 것이 중요하다.
- 함수의 마지막 인자가 함수인 경우 후행 람다 전달을 사용할 수 있다.
- 가장 많이 사용해왔던 표현식으로, 함수의 정의를 따로 하지 않고, 람다식으로 바로 작성할 수 있다.
- 람다 함수의 파라미터가 1개인 경우에만 it 키워드를 사용할 수 있다.
- 2개 이상을 파라미터가 존재한다면, 파라미터를 명시적으로 작성해서 사용해야 한다
람다 레퍼런스
fun main() {
//val callReference: () -> Unit = { printHello() }
val callReference = ::printHello //람다 레퍼런스 구문을 사용
callReference()()
val numberList = listOf("1", "2", "3")
//numberList.map { it.toInt() }.forEach { println(it) }
numberList.map(String::toInt).forEach(::println) //람다 페퍼런스를 사용해 쉽게 표현가능
//탑레벨 함수일 경우 람다 레퍼런스 앞에 아무것도 없이 사용하면 되고, 확장함수나,클래스 맴버 함수일 경우 람다 레퍼런스 앞에 클래스 이름을 쓰면 된다.
//코틀린에서는 애로우라는 함수형 라이브러리를 적용한다면, 쉽게 함수형 프로그래밍을 작성할 수 있다.
}
val printHello: () -> Unit = { println("Hello") }
- 람다 레퍼런스를 사용하면 가독성 좋게 함수를 인자로 넘길 수 있다(::)
- 탑-레벨, 로컬 함수는 ::연산자 앞에 아무것도 없이 ::함수명 을 사용하고, 클래스의 멤버, 확장 함수일 경우 ::연산자 앞에 클래스 지시자를 사용해 클래스::함수명을 사용한다.
애로우
Λrrow
Functional companion to Kotlin's Standard Library
arrow-kt.io
- 코틀린에서 함수형 프로그래밍을 위한 라이브러리이다.
- 함수형 프로그래밍의 다양한 개념 및 기능들을 제공하고, 점점 사용 사례가 증가하고 있다.
728x90
'실무 프로젝트로 배우는 Kotlin & Spring > 코틀린 고급' 카테고리의 다른 글
| 고급 예외처리 (0) | 2022.10.22 |
|---|---|
| 스코프 함수 (0) | 2022.10.22 |
| 페어와 구조분해할당 (0) | 2022.10.16 |
| 지연 초기화 (0) | 2022.10.16 |
| 제네릭 (0) | 2022.10.16 |