본문 바로가기
Language/코틀린

[Kotlin] Fast campus 강의 Android App 개발 (코틀린) 함수형 프로그래밍, .let .apply .run .also , Closure , 확장함수 , DSL , 커링(currying)

by javapp 자바앱 2021. 10. 24.
728x90

 

함수형 프로그래밍

 

 

순수함수와 고차함수

순수함수 : 결과가 같은 함수, 외부 레퍼런스 사용하지 않는 함수, 입력된 값의 결과는 언제나 같아야 한다.
- 전역적 참고 X, 입력받아 처리 (변수 객체 함수..)

고차함수 : 함수형 변수를 입력 받고 함수형 변수를 리턴 ,상태(= 조건문)없는 순차적인 프로그래밍을 위해 함수를 받고 함수를 리턴하는 구조

 

 

// 함수를 매개변수로 받는 함수
fun fn1(func : (Int, Int)-> Int){
    func(10,10).let{result -> println("결과값은 $result 입니다.")}
}

fun fn2(func : (Int, Int)-> Int){
    println(func(10,10)) // n + n2
}

fun fn3(func : (Int, Int)-> Int, a : Int, b : Int){
    println(func(a,b))
}

fun fn4(func : (Int) -> Int){
    println(func(1))
}

fun add100(i : Int) : Int {return i + 100}

 

 

 

 

1. 람다식으로 정의한 함수형 변수
30


2. 고차함수에 람다식을 넘기기
결과값은 20 입니다.
20


3. 고차함수에 람다식과 값을 같이 넘기기
300


4. 함수의 주소값을 직접 대입 시키는 방법
101


5. 람다식의 간소화
101
101

101

 

 

 

 

 

 

 


 

 

주요 확장 함수 ( Scope Functions )

let, apply , run, also

 

 

 

1. let : 모든 객체에 확장 함수로 사용 가능

 

fun f1( i : Int) : Int { return i + 1}
    var n = 1
    
    // 일반적인 호출 형태
    var rst = f1(n)
    println(rst) // 을 단순화

    f1(n).let{value -> println(value)}
    f1(n).let{ println(it) } // 변수명을 지정하지 않을때

2
2
2

 

 

 

스트림 형식

    123.let{it +5}.let { it * 3 }.let { println(it) }

384

 

 

?

    // null 일 때 실행 안됨.
    var bug : () -> Any? = {null}
    bug()?.let { println(it) }

 

 

 

2. apply()

 

함수 내에 this 를 넘겨 활용
객체 생성과 동시에 초기화 할 때 많이 사용

 

class Test
{
    var name : String = "무명"
    var age : Int = 0

    fun aboutMe() = println("이름은 \"$name\" 이고 나이는 \"$age\" 입니다.")
    fun aging() = age++
}

Test class 하나를 정의

 

    var obj = Test().apply { name= "김모씨"; age= 49 }
    obj.aboutMe()

이름은 "김모씨" 이고 나이는 "49" 입니다.

 

함수내에 this.name , this.age 이런 식으로 클래스 변수에 접근 연산 가능

 

 

 

 

3. run()

 

함수내에 연산을 하고 넘기고 싶은 값을 마지막에 작성

    obj.run { name="이모씨"; aging(); name }.let{println(it)}

이모씨

 

 

많이 쓰는 패턴
결과를 변수 없이 넘길때

    run{333 + 4}.let { println(it) }

337

 

 

 

4. also()

 

반환 값 없이 실행

    10.let{println(it) ; it}
        .also{println(it + 1000)}   // 1100
        .also{println(it + 20)}     // 100 + 20
        .let{println(it)}           // 100

10
1010
30
10

 

 

 


 

 

Closure

 

 

closure
*외부함수의 변수를 내부함수에서 그대로 기억하고 사용할 수 있다.
마치 객체처럼 함수를 사용할 수 있다.

 

 

case 1

// case 1
// 반환 값이 익명함수
fun closure1(num : Int) : (Int) -> Int
{
    var sum : Int = num

    // sum을 전역함수로 쓰는 함수
    return fun(num2 : Int) : Int{
        sum += num2
        return sum
    }
}
    // case 1
    var fn = closure1(10)
    println( fn(10) )
    println( fn(10) )	// closure1(10) 의 var sum 의 값을 계속 기억 하고 있다.

20
30

 

 

 

 

 

case 2

// case2
// 많이 쓰는 경우
// 함수 받고 함수 반환
fun closure2(pFunc : (Int) -> Int) : () -> Int
{
    var result : Int = 0

    return {
        result =pFunc(result)
        result
    } // () -> Int
}
    // case 2
    // 특정 로직 바껴지고 함수로 관리 해야 될 경우
    var decByOne = closure2({num -> num -1})
    println( decByOne())
    println( decByOne())

    fun Add(num : Int) = num + 1
    var addByTen = closure2(::Add)
    println(addByTen())
    println(addByTen())

-1
-2
1
2

 

 

 


 

 

확장함수

 

 

 

확장함수
확장함수는 이미정의된 클래스에 멤버함수를 정의
확장함수를 실행함과 동시에 객체정보를 공유
확장함수로 넘겨진 파라미터는 val(읽기전용) 으로 설정
확장함수에 파라미터로 함수를 넘기면 객체설정 및 이벤트 핸들러를 구현하기 편해진다.

 

 

 

 

확장함수 형태가 반환값없는 Animal.() 형태

class Animal
{
    open var count = 0

    fun crying() = println("$this >>> 어흥")
    fun eat() = println("$this >> 우걱우걱")
}
fun ani(n : Int, extFunc : Animal.(Int)->Unit) : Animal
{
    var animal = Animal()

    // 매우 중요
    animal.extFunc(n)
    return animal
}
    // 2. 파라미터로 확장함수 -> 객체에서 실행
    // 생성과 동시에 초기화
    var obj = ani(3, {
        crying()
        eat()
        count += it
    })

org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >>> 어흥
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >> 우걱우걱

 

 

 

 

4. Android 에서 흔하게 보게될 코드 (Interface와 함께)

class Animal
{
    open var count = 0

    fun crying() = println("$this >>> 어흥")
    fun eat() = println("$this >> 우걱우걱")

    // 4.
    open fun setOnEvent(count : Int, message : String, extFunc : Animal.(Int)-> Unit) : Animal
    {
        when(message)
        {
            "울어" -> {extFunc(count)} // 넘겨진 확장함수 실행
            "먹어" -> {
                var n = if (count < 3) 3 else count
                extFunc(n)
            }
            else -> {println("X : ${message}")}
        }
        return this
    }
}
    obj.setOnEvent(3, "울어",
        {
            count ->
            println("${count} 번 웁니다.")
            (1..count).map { crying() }
        }
    )

    obj.setOnEvent(2, "먹어",
        {
            n ->
            println("$n 번 먹음")
            (1..n).map{ eat() }
        }
     )

3 번 웁니다.
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >>> 어흥
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >>> 어흥
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >>> 어흥
3 번 먹음
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >> 우걱우걱
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >> 우걱우걱
org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$Animal@52d455b8 >> 우걱우걱

 

 

 

 

 

확장함수.. 어렵다.. 이해가 잘 안된다.

자주 보아서 익숙해질 수 밖에 없을 듯..

 

 

 


 

 

 

DSL

Domain-specific language

특정 애플리케이션이나 Software 에서 사용할 목적으로 만든 간이언어
반복되고 제한된 환경을 위한 간이언어
Kotlin 에서는 확장함수를 사용하여 구현 가능함
.

 

data class

data class Player(var name : String? = null, var status : Status? = null)

data class Status(var job : String? = null, var level : Int? = null)

 

// DSL 위한 확장함수
fun MakePlayer(block: Player.() -> Unit) : Player{
    return Player().apply(block)
}

fun Player.status(block : Status.() -> Unit){
    status = Status().apply(block)
}

 

fun main()
{
    var p0 = Player()
    p0.name = "Player 0"
    p0.status = Status()
    p0!!.status!!.job = "Wizard"
    p0!!.status!!.level = 0
    println(p0)


    // data class 로 정의
    var p1 = Player("Player 1", Status("Archer", 23))

    // DSL
    var p2 = MakePlayer {
        name = "Player 2 - " + 2.toString()
        status {
            job = "Paladin"
            level = 50
        }
    }
    println(p2)
}

<실행 결과>

layer(name=Player 0, status=Status(job=Wizard, level=0))
Player(name=Player 2 - 2, status=Status(job=Paladin, level=50))

 

 

 

 


 

 

커링 currying

 

다중 인자를 받는 함수를 단일 인자를 받는 함수로 만드는 함수
소스코드 읽을 때 이런 문법이 있다 정도

 

 

데이터형 : (인자) ->(인자). .. -> 리턴값
함수 정의 : 함수(a) : 리턴값 = 함수(b)= 함수(c) : 리턴값 {}

 

 

커링 함수 정의

// 커링은 파라미터 1개
fun workForMoney( moneyByHour : Double) : (Int) -> (String) -> String
    = fun(time : Int)
    = fun(job : String) : String{
        return if (time > 8 * 5 * 4)
            "{$job} 님은 돈이 문제가 아님."
            else "{$job} 입니다. ${moneyByHour * time}만큼 받습니다."
}

 

메인 실행문

fun main()
{
    //정의문
    //workForMoney 복사
    val alba : (Double)->(Int)->(String)->String =::workForMoney

    // 첫번째 파라미터까지 실행 workForMoney(Double)
   val normal_people : (Int)-> (String)->String
            = workForMoney(25000.0)
    val developter : (Int)->(String)->String
            = workForMoney(35000.23)
    val highIncome : (String)->String
            = workForMoney(45000.23)(8*5*5)


    //실행문
    // 함수 타입
    listOf<()->String>(
        {alba(8690.0)(8 * 5 * 4)("알바")},
        {normal_people(8*5*4)("일반인")},
        {developter(8*5*4)("개발자")},
        {highIncome("고양이가 세상을 구한다.")},
        {"퇴직자 희망사항 : "+workForMoney(3000.0)(8*5*4)("백수")}
    ).map{it().let{result-> println(result)}}
}

 

<실행 결과>

 

{알바} 입니다. 1390400.0만큼 받습니다.
{일반인} 입니다. 4000000.0만큼 받습니다.
{개발자} 입니다. 5600036.800000001만큼 받습니다.
{고양이가 세상을 구한다.} 님은 돈이 문제가 아님.
퇴직자 희망사항 : {백수} 입니다. 480000.0만큼 받습니다.

 

 

 

 

 

여기까지

함수형 프로그래밍에서 순수함수와 고차함수

주요 확장 함수에서 .let .apply .run .also

Closure

확장함수

DSL

커링(currying) 에 대해서 알아봤습니다.

감사합니다.

 

 

 

fast campus - 올인원 패키지 : 안드로이드 앱 개발

댓글