Kotlin Study 04_1

박채빈·2021년 9월 16일
0

KotlinStudy

목록 보기
4/7
post-thumbnail

IDE : Intellij
JDK : zulu11

Kotlin 함수와 함수형 프로그래밍 1

함수

함수는 여러 값을 입력받아 기능을 수행하고 결괏값을 반환하는 코드의 모음이다.

sumFunc.kt

fun sum1(a: Int, b: Int): Int {
   val sum = a + b
   return sum
}

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

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

두 정수를 받아 그 합을 return하는 함수. sum1, sum2, sum3 모두 같은 의미이다.

VarargsTest.kt

fun normalVarargs(vararg counts: Int){
   for(num in counts){
       print("$num ")
   }
   println()
}

fun main(args: Array<String>) {
   normalVarargs(1, 2, 3, 4)
   normalVarargs(4, 5, 6)
}

vararg 키워드를 사용하여 가변 인자를 받을 수 있다.
실행결과 :

1 2 3 4 
4 5 6 

Unit

함수의 반환값이 없을 때 반환값의 자료형을 Unit으로 지정하거나 생략할 수 있다.

fun printSum(a: Int, b: Int): Unit{
	println("sum of $a and $b is ${a + b}")
}

fun printSum(a: Int, b: Int) {
	println("sum of $a and $b is ${a + b}")
}

둘 다 같은 의미
Java의 void와 대응하나, void는 정말 아무것도 반환하지 않는 것이고, Unit은 특수한 객체를 반환한다는 차이점이 있다.


함수형 프로그래밍

코틀린은 함수형 프로그래밍(FP)과 객체 지향 프로그래밍(OOP)을 모두 지원하는 다중 패러다임 언어이다.
FP는 순수 함수를 작성하여 프로그램의 부작용을 줄이는 프로그래밍 기법이다. 람다식과 고차 함수를 사용한다.

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

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

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

순수 함수

어떤 함수가 같은 인자에 대하여 항상 같은 결과를 반환하면 부작용 없는 함수라 한다. 부작용 없는 함수가 함수 외부의 어떤 상태도 바꾸지 않는다면 순수 함수(Pure Function)이라 한다.
순수 함수는 스레드에 사용해도 안전하고 코드 테스트도 쉽다.

순수함수의 조건

  1. 같은 인자에 대하여 항상 같은 값을 반환한다.
  2. 함수 외부의 어떤 상태도 바꾸지 않는다.
fun sum(a: Int, b: Int): Int{
	return a+b
}

매개변수 a, b를 이용하여 덧셈 연산 후 그 값을 그대로 반환하는 순수함수

fun check(){
	val test = User.grade()	// check 함수 내에 없는 외부 객체 사용
    if(test != null)	process(test)	// 변수 test는 User.grade()의 결과에 따라 달라짐
}

순수 함수 조건을 만족하지 못함

람다식

FP에서의 람다식은 다른 함수의 인자로 넘기는 함수, 함수의 결괏값으로 반환하는 함수, 변수에 저장하는 함수를 말한다.

일급 객체

FP에서는 함수를 일급 객체로 생각한다. 만약 함수가 일급 객체면 일급 함수라 부른다.
일급 함수에 이름이 없는 경우 람다식이라 부른다.

일급 객체의 조건

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

고차 함수

다른 함수를 인자로 사용하거나 함수를 결괏값으로 반환하는 함수를 말한다.
즉, 일급 객체 혹은 함수를 서로 주고받을 수 있는 함수이다.

fun main(){
	println(highFunc({x, y -> x + y}, 10, 20)
}

fun highFunc(sum: (Int, Int)->Int, a: Int, b: Int): Int = sum(a, b)

highFunc() 함수는 sum을 통해 람다식 함수를 인자로 받아들일 수 있는 고차함수이다.

FuncArgument.kt

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

fun main(args: Array<String>) {
    val res1 = sum(3, 2)    // 일반 인자
    val res2 = mul(sum(3,3), 3) // 인자에 함수 사용

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

인자로 일반 함수를 전달

FuncFunc.kt

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

fun funcFunc(): Int{
    return sum(2, 2)
}

fun main(args: Array<String>) {
    println("funcFunc: ${funcFunc()}")
}

반환값으로 sum() 함수를 사용

HighOrderTest.kt

fun main(args: Array<String>) {
    val result: Int
    val multi = {x: Int, y: Int -> x * y}

    result = multi(10, 20)
    print(result)
}

변수 multi는 (Int, Int)->Int로 선언된 람다식에 의해 Int형으로 자료형이 추론된다.
람다식이 변수에 할당되어 multi() 와 같이 함수 형태로 사용할 수 있다.

// 모두 같은 표현
val multi: (Int, Int)->Int = {x: Int, y: Int -> x * y} // 전체 표현
val multi = {x: Int, y: Int -> x * y}	// 선언 자료형 생략
val multi: (Int, Int)->Int = {x, y -> x * y}	// 람다식 매개변수 자료형 생략
// 반환 자료형이 없거나 매개변수가 하나만 있을 때
val greet: ()->Unit = {println("Hello World!")
val square: (Int)->Int = {x -> x*x}

HighOrderTest2.kt

fun highOrder(sum: (Int, Int)->Int, a: Int, b: Int) = sum(a ,b)

fun main(args: Array<String>) {
    val result: Int = highOrder({ x, y -> x + y}, 10, 20)
    println(result)
}

람다식을 매개변수에 사용한 고차함수

HighOrderTest3.kt

fun main(args: Array<String>) {
    val out: ()->Unit = {println("Hello World!")} // ()->Unit 생략가능
    out()
    val new = out
    new()
}

인자와 반환값이 없는 람다식

CallByValue.kt

fun main(args: Array<String>) {
    val result = callByValue(lambda())	// 람다식 함수를 호출
    println(result)
}

fun callByValue(b: Boolean): Boolean{	// 일반 변수 자료형으로 선언된 매개변수
    println("callByValue function")
    return b
}

val lambda: ()->Boolean = {
    println("lambda function")
    true
}
  1. main() > result > callByValue의 파라미터 lambda() 우선 수행
  2. lambda()의 출력 구문 수행 후, 반환값(true)을 callByValue의 파라미터로 전달
  3. callByValue의 출력 구문 수행 후, 파라미터로 받은 b를 반환하여 main() > result에 할당
  4. main() 의 출력 구문 수행

출력결과

lambda function
callByValue function
true

CallByName.kt

fun main(args: Array<String>) {
    val result = callByName(otherLambda)	// 람다식 이름으로 호출
    println(result)
}

fun callByName(b: ()->Boolean): Boolean {	// 람다식 자료형으로 선언된 매개변수
    println("callByName function")
    return b()
}

val otherLambda: ()->Boolean = {
    println("otherLambda function")
    true
}
  1. main() > result > callByName의 파라미터 otherLambda식 자체가 매개변수에 복사됨
  2. callByName의 출력 구문 수행 후, return의 b() 람다식 호출
  3. otherLamber의 출력 구문 수행 후, 반환값(true)을 전달
  4. callByName의 반환값을 main() > result 에 할당
  5. main()의 출력 구문 수행

출력결과

callByName function
otherLambda function
true

FunctionReference.kt

fun main(args: Array<String>) {
    // 1. 인자와 반환값이 있는 함수
    val res1 = funcParam(3, 2, ::sum)
    println(res1)

    // 2. 인자가 없는 함수
    hello(::text)

    // 3. 일반 변수에 값처럼 할당
    val likeLambda = ::sum
    println(likeLambda(6,6))
}

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

fun text(a: String, b: String) = "Hi! $a $b"

fun funcParam(a: Int, b: Int, c: (Int, Int)->Int): Int = c(a ,b)

fun hello(body: (String, String)->String): Unit = println(body("Hello", "World"))
  1. funParam(3, 2, ::sum) : 인자 3, 2가 sum에 전달되어 5를 반환한다.
  2. hello(::text) : 반환값이 없으며 hello() 함수 선언부의 body() 함수와 함수에 인자로 전달한 2개 문자열이 'hi!'와 결합되어 출력된다.
  3. ::sum을 변수에 할당

:: 를 이용한 표기법

hello(::text)
hello({a, b -> text(a, b)})
hello{a, b -> text(a, b)}

ParamCount.kt

fun main(args: Array<String>) {
    // 매개변수 없는 람다식
    noParam({ "Hello World!" }) // 소괄호 생략가능. intellij에서 애당초 소괄호 안만들어줌.
    noParam { "Hello World!" }

    // 매개변수 하나 있는 람다식
    oneParam { a -> "Hello World! $a" }
    oneParam { "Hello World! $it" }

    // 매개변수 2개 있는 람다식
    moreParam { a, b-> "Hello World! $a $b"}
    moreParam { _, b-> "Hello World! $b"}   // 특정 매개변수를 사용하지 않을 때 '_' 사용

    withArgs("Arg1", "Arg2", {a, b -> "Hello World! $a $b"})
    // 마지막 인자가 람다식인 경우, 소괄호 밖으로 분리 가능. intellij에서 이걸 권장
    withArgs("Arg1", "Arg2") {a, b -> "Hello World! $a $b"}
}

fun noParam(out: ()->String) = println(out())

fun oneParam(out: (String)->String) = println(out("OneParam"))

fun moreParam(out: (String, String)->String) = println(out("OneParam", "TwoParam"))

// 일반 매개변수 2개 포함, 마지막 매개변수로 람다식을 가짐
fun withArgs(a: String, b: String, out: (String, String)->String) = println(out(a, b))

TwoLambdaParam.kt

fun main(args: Array<String>) {
    twoLambda({a, b -> "First $a $b"}, {"Second $it"})
    twoLambda({a, b -> "First $a $b"}) {"Second $it"}
}

fun twoLambda(first: (String, String)->String, second: (String)->String){
    println(first("OneParam", "TwoParam"))
    println(second("OneParam"))
}

2개의 람다식을 매개변수로 가진 함수의 경우에도 위 ParamCount.kt와 같이 사용할 수 있다.
람다식을 3개 가지는 경우에도 아래와 같이 사용 가능하다.

({first}, {second}) {third}
profile
안드로이드 개발자

0개의 댓글