[앱프정리] 5주차

Jingu_Jeon·2024년 11월 2일

앱프 스터디

목록 보기
5/5

코틀린람다 컬렉션

이 코드는 Kotlin에서 고차 함수와 함수 참조(Function Reference)를 사용하는 방법을 보여줍니다. display, greet, circleArea, cubic, root, compute 함수가 정의되어 있으며, 이를 활용해 고차 함수의 기능을 시연하고 있습니다. 각 함수의 역할을 설명드리겠습니다.

함수 설명

display 함수

fun display(name: String) {
    println("Hello, $name!")
}

입력받은 name을 이용해 "Hello, name!" 메시지를 출력하는 함수입니다.

greet 함수 (고차 함수)

fun greet(name: String, action: (String) -> Unit) {
    action(name)
}

greet 함수는 name과 action이라는 함수를 매개변수로 받습니다.
action은 String을 입력받아 반환값이 없는 함수 형태이며, greet 함수는 action(name)을 호출하여 전달받은 함수를 실행합니다.

circleArea, cubic, root 함수 (수학 관련 함수들)

fun circleArea(radius: Double): Double = Math.PI * radius * radius
fun cubic(x: Double): Double = x * x * x
fun root(x: Double): Double = Math.sqrt(x)

circleArea: 원의 반지름 radius를 받아 원의 넓이를 계산하여 반환합니다.
cubic: 주어진 x 값을 세제곱하여 반환합니다.
root: 주어진 x 값의 제곱근을 반환합니다

compute 함수 (고차 함수)

fun compute(x: Double, f: (Double) -> Double): Double = f(x)

compute 함수는 x와 f라는 함수를 매개변수로 받아, f(x)를 계산하여 반환합니다.
f는 Double을 입력받아 Double을 반환하는 함수입니다.

main 함수

fun main() {
    display(name = "Alice")
    
    val myfunct: (String) -> Unit = ::display
    myfunct("Bob")

    greet(name = "Alice", ::display)

    val funs = listOf(::circleArea, ::cubic, ::root)
    for (f in funs) {
        println(compute(x = 2.0, f))
    }
}
  • display(name = "Alice"): display 함수를 호출하여 "Hello, Alice!"를 출력합니다.

  • val myfunct: (String) -> Unit = ::display:

    • display 함수를 참조하여 myfunct라는 변수에 할당합니다.
    • myfunct("Bob")를 호출하면 display("Bob")과 동일하게 동작하여 "Hello, Bob!"이 출력됩니다.
  • greet(name = "Alice", ::display):

    • greet 함수에 display 함수를 참조하여 전달합니다.
    • greet 함수는 display("Alice")를 호출하게 되므로, "Hello, Alice!"가 출력됩니다.
  • val funs = listOf(::circleArea, ::cubic, ::root):

    • circleArea, cubic, root 함수를 참조하여 리스트로 저장합니다.

    • for (f in funs) 루프를 통해 funs 리스트의 각 함수에 대해 compute(x = 2.0, f)를 호출합니다.

    • compute(2.0, f)는 f(2.0)의 결과를 반환하므로, 각 함수에 대해 다음과 같은 결과를 출력하게 됩니다:

      • circleArea(2.0) : 원의 넓이 계산 결과
      • cubic(2.0) : 2.0의 세제곱
      • root(2.0) : 2.0의 제곱근

출력 예시

프로그램을 실행하면 다음과 같은 출력이 예상됩니다:

Hello, Alice!
Hello, Bob!
Hello, Alice!
12.566370614359172  // circleArea(2.0) 결과
8.0                 // cubic(2.0) 결과
1.4142135623730951  // root(2.0) 결과

이 예제는 Kotlin에서 함수 참조와 고차 함수를 활용하여 다양한 함수를 동적으로 호출하는 방법을 보여줍니다. :: 연산자를 사용해 함수의 참조를 쉽게 전달할 수 있고, 이를 통해 유연한 함수 호출이 가능합니다.


코틀린 람다 컬렉션(본격)

하이워드 펑션, 람다 컬렉션


이 코드는 Kotlin에서 람다 표현식, 함수 리터럴, 고차 함수, 그리고 forEach를 사용하는 다양한 방법을 보여줍니다. 각 부분을 설명드리겠습니다.

람다 표현식 및 함수 리터럴 정의

f 람다 표현식

val f: (String) -> Unit = { name: String -> println("Hello, $name!") }

String 타입의 매개변수를 받아서, "Hello, name!" 형식으로 출력하는 람다 표현식입니다.

simple 함수 리터럴

val simple = { println("Hello, Kotlin!") }

매개변수를 받지 않고 "Hello, Kotlin!"을 출력하는 함수 리터럴입니다.
Function Literal with Receiver: 수신 객체가 없는 단순한 함수 리터럴로 볼 수 있습니다.

수학 관련 람다 함수들

val circleArea2 = { radius: Double -> Math.PI * radius * radius }
val cubic2 = { x: Double -> x * x * x }
val root2 = { x: Double -> Math.sqrt(x) }

circleArea2: 원의 넓이를 계산하는 함수로, 반지름 radius를 받아 Math.PI radius radius를 계산합니다.
cubic2: 주어진 x 값을 세제곱하여 반환합니다.
root2: 주어진 x 값의 제곱근을 반환합니다.

chooseFun 함수 (고차 함수)

fun chooseFun(choice: Int) = when (choice) {
    1 -> circleArea2
    2 -> cubic2
    else -> ::root
}

choice 값을 기준으로 해당하는 함수를 반환합니다.
1이면 circleArea2, 2이면 cubic2, 그 외에는 root 함수를 반환합니다.

main 함수

fun main() {
    display(name = "Alice")
    
    val myfunct: (String) -> Unit = ::display
    myfunct("Bob")

    greet(name = "Alice", ::display)

    val funs = listOf(::circleArea, ::cubic, ::root)
    for (f in funs) {
        println(compute(x = 2.0, f))
    }

    val a = chooseFun(choice = 1)(3.0)
    println(a)

    val lambdas = listOf(circleArea2, cubic2, root2)
    for (f in lambdas) {
        println(compute(x = 2.0, f))
    }

    val arr = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    arr.forEach { println(it * 2) }
}
  • display(name = "Alice"): display 함수가 호출되어 "Hello, Alice!"가 출력됩니다.

  • val myfunct: (String) -> Unit = ::display:

    • display 함수를 참조하여 myfunct 변수에 할당합니다.
    • myfunct("Bob")을 호출하면 "Hello, Bob!"이 출력됩니다.
  • greet(name = "Alice", ::display):

    • greet 함수에 display 함수를 전달하여 호출합니다. "Hello, Alice!"가 출력됩니다.
  • funs 리스트와 compute 함수:

    • funs 리스트는 circleArea, cubic, root 함수를 참조하여 저장합니다.
    • for (f in funs) 루프를 통해 각 함수에 대해 compute(x = 2.0, f)를 호출하여 결과를 출력합니다.
  • val a = chooseFun(choice = 1)(3.0):

    • chooseFun 함수를 호출하여 choice 값에 따라 선택된 함수를 3.0 인자로 호출합니다.
    • 예를 들어, choice = 1이면 circleArea2(3.0)이 호출됩니다.
  • lambdas 리스트와 compute 함수:

    • lambdas 리스트는 circleArea2, cubic2, root2 람다 표현식을 저장합니다.
    • for (f in lambdas) 루프에서 compute(x = 2.0, f)를 호출하여 결과를 출력합니다.

forEach와 람다식

val arr = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
arr.forEach { println(it * 2) }

arr 배열의 각 요소에 대해 it * 2 값을 출력하는 람다식을 사용하여 forEach 메서드를 호출합니다.
it은 forEach 블록 내부에서 배열의 각 요소를 나타내는 키워드입니다.

출력 예시

프로그램을 실행하면 예상되는 출력은 다음과 같습니다:

Hello, Alice!
Hello, Bob!
Hello, Alice!
12.566370614359172  // circleArea(2.0) 결과
8.0                 // cubic(2.0) 결과
1.4142135623730951  // root(2.0) 결과
28.274333882308138  // circleArea2(3.0) 결과
12.566370614359172  // circleArea2(2.0) 결과
8.0                 // cubic2(2.0) 결과
1.4142135623730951  // root2(2.0) 결과
2
4
6
8
10
12
14
16
18

이 예제는 Kotlin에서 함수 참조, 람다 표현식, 고차 함수, 그리고 forEach의 사용법을 잘 보여줍니다. :: 연산자를 통해 함수를 참조할 수 있고, when을 이용해 함수 선택기를 구현할 수도 있습니다. 이러한 기능을 통해 코드를 보다 유연하고 간결하게 작성할 수 있습니다.


일반함수와 람다함수 비교관점

람다 함수와 일반 함수의 차이점을 관점별로 비교해 보겠습니다. circleArea2, cubic2, root2는 람다 함수로 작성된 예제이고, 이와 동일한 기능을 일반 함수로도 작성할 수 있습니다. 두 가지 방식의 차이점과 장단점을 살펴보겠습니다.

1. 문법과 선언 방식

람다 함수

val circleArea2 = { radius: Double -> Math.PI * radius * radius }
val cubic2 = { x: Double -> x * x * x }
val root2 = { x: Double -> Math.sqrt(x) }

람다 함수는 val이나 var로 선언한 후, { 매개변수 -> 표현식 } 형식으로 작성됩니다.
일반 함수와 달리 함수 이름을 따로 정의하지 않아도 됩니다.
코드가 간결해지는 장점이 있습니다.

일반 함수

fun circleArea(radius: Double): Double = Math.PI * radius * radius
fun cubic(x: Double): Double = x * x * x
fun root(x: Double): Double = Math.sqrt(x)

일반 함수는 fun 키워드와 함수 이름을 사용해 정의합니다.
매개변수와 반환 타입을 명확하게 지정해야 합니다.
코드가 좀 더 명시적이며, 이름을 붙여서 호출하기 쉽습니다.

2. 타입 추론

람다 함수

val circleArea2 = { radius: Double -> Math.PI * radius * radius }

람다 함수의 경우, 매개변수 타입은 명시적으로 지정하지만, 반환 타입은 추론됩니다.
val circleArea2의 전체 타입을 명시적으로 적으려면 다음과 같이 작성할 수 있습니다:

val circleArea2: (Double) -> Double = { radius -> Math.PI * radius * radius }

간결한 코드를 작성할 수 있지만, 큰 규모의 코드에서는 가독성이 떨어질 수 있습니다.

일반 함수

fun circleArea(radius: Double): Double = Math.PI * radius * radius

일반 함수에서는 매개변수와 반환 타입을 명확하게 지정해야 합니다.
타입이 명시적이므로 코드 읽기가 더 쉬워질 수 있으며, 코드의 가독성을 높일 수 있습니다.

3. 사용 위치와 함수 참조

람다 함수
람다 함수는 변수에 저장하여 전달하거나, 리스트와 같은 컬렉션에 추가하여 동적으로 사용할 수 있습니다.

고차 함수의 매개변수로 쉽게 전달할 수 있습니다.

val lambdas = listOf(circleArea2, cubic2, root2)
for (f in lambdas) {
    println(f(2.0))
}

일반 함수
일반 함수는 함수 이름을 통해 참조할 수 있습니다.

함수 참조(::)를 사용하여 고차 함수에 전달할 수 있습니다.

val functions = listOf(::circleArea, ::cubic, ::root)
for (f in functions) {
    println(f(2.0))
}

4. 스코프와 가독성

람다 함수
람다 함수는 주로 짧고 간단한 동작을 정의할 때 유용합니다.
작은 스코프(예: 지역 변수)에서 일시적으로 사용할 함수를 정의할 때 적합합니다.
코드가 복잡해질 경우 람다 표현식은 가독성을 떨어뜨릴 수 있습니다.
일반 함수
일반 함수는 코드의 재사용성을 높일 수 있습니다.
여러 곳에서 반복적으로 호출될 때 명시적인 함수 이름이 있어서 코드 이해가 쉽습니다.
큰 스코프에서도 함수의 목적을 명확하게 드러낼 수 있어 가독성이 좋습니다.

5. 인라인 가능성

람다 함수
람다 함수를 사용하는 고차 함수에 inline 키워드를 추가하면, 성능 최적화를 할 수 있습니다.
람다 함수를 인라인(inline) 처리하여 함수 호출 오버헤드를 줄일 수 있습니다.
일반 함수
일반 함수는 inline 키워드를 사용할 수 없습니다.
반복 호출되는 경우, 일반 함수는 함수 호출 스택이 늘어나 성능에 영향을 줄 수 있습니다.

6. 변경 불가능성 (Immutable)

람다 함수
람다 함수는 일반적으로 val로 선언되기 때문에 변경이 불가능(immutable)합니다.
불변성을 보장할 수 있으며, 코드의 안정성이 높아집니다.
일반 함수
일반 함수는 코드 자체는 변경할 수 없지만, 재정의(override)가 가능한 구조를 가질 수 있습니다.
인터페이스나 추상 클래스에서 일반 함수를 재정의하여 다른 동작을 구현할 수 있습니다.

결론

간결함이 필요한 경우: 람다 함수는 간단한 표현식이나 짧은 코드 블록에서 유용합니다. 특히 일회성으로 사용하는 기능을 정의할 때 적합합니다.
가독성과 재사용성이 중요한 경우: 일반 함수는 가독성이 좋고 코드가 반복해서 사용될 때 유리합니다. 함수 이름을 통해 기능을 명확히 할 수 있으며, 큰 규모의 코드에서는 일반 함수가 더 권장됩니다.
함수 참조가 필요한 경우: 일반 함수와 람다 함수 모두 함수 참조로 사용할 수 있지만, 일반 함수는 직접 ::로 참조할 수 있어 코드가 명확해지는 장점이 있습니다.
두 방법 모두 고유한 장단점이 있으므로, 상황에 따라 적합한 방식을 선택하는 것이 중요합니다.


람다 컬렉션 추가


이 코드는 Kotlin에서 data class, List, MutableList, Map, MutableMap을 사용하는 예제입니다. Menu라는 데이터를 기반으로 여러 컬렉션에 데이터를 추가하고, 이를 순회하며 출력하는 방법을 보여줍니다. 각 코드 블록을 설명드리겠습니다.

data class Menu(val name: String, val price: Int)

Menu는 name과 price라는 두 개의 속성을 가진 data class입니다.
data class는 자동으로 toString, equals, hashCode 등을 제공하므로, 데이터를 다루기 편리합니다.

List와 Iterator 사용 예제

1. 불변 리스트 생성 및 순회

val menus = listOf(
    Menu(name = "짜장면", price = 8000),
    Menu(name = "짬뽕", price = 9000),
    Menu(name = "군만두", price = 8000),
    Menu(name = "물만두", price = 7000)
)
for (m in menus) {
    println(m)
}

listOf를 사용하여 불변 리스트 menus를 생성합니다.
각 메뉴는 Menu 객체로 초기화됩니다.
for 루프를 통해 menus 리스트를 순회하며 각 메뉴 항목을 출력합니다.

2. Iterator를 이용한 순회

val iterator = menus.iterator()
while (iterator.hasNext()) {
    println(iterator.next())
}

menus.iterator()를 사용하여 Iterator 객체를 생성하고, while 루프를 통해 순회합니다.
iterator.next()를 통해 각 요소를 하나씩 출력합니다.

MutableList 사용 예제

val board = mutableListOf<Menu>()
board.add(Menu(name = "삼계탕", price = 7500))
board.add(Menu(name = "김치찌개", price = 6000))
board.add(Menu(name = "규동", price = 9000))
for (m in board) {
    println(m)
}

mutableListOf를 사용하여 가변 리스트 board를 생성합니다.
board.add(...)를 통해 메뉴 항목을 추가합니다.
for 루프를 통해 board 리스트를 순회하며 각 항목을 출력합니다.

MutableMap 사용 예제

val board2 = mutableMapOf<String, Menu>()
board2.put("1", Menu(name = "짜장면", price = 8000))
board2.put("2", Menu(name = "짬뽕", price = 9000))
board2.put("3", Menu(name = "군만두", price = 8000))
board2.put("4", Menu(name = "물만두", price = 7000))

println(board2["1"])

for (key in board2.keys) {
    println(board2[key])
}
  • mutableMapOf를 사용하여 가변 맵 board2를 생성합니다.
  • put 메서드를 사용하여 각 메뉴를 키-값 쌍으로 추가합니다.
    • 예를 들어, "1" 키에는 Menu(name = "짜장면", price = 8000)이 저장됩니다.
  • println(board2["1"])를 통해 키 "1"에 해당하는 메뉴를 출력합니다.
  • for (key in board2.keys) 루프를 통해 board2의 모든 키를 순회하며 각 메뉴를 출력합니다.

중첩된 List를 가진 Map 생성

val map = mapOf(
    "1" to listOf(Menu(name = "짜장면", price = 8000), Menu(name = "짬뽕", price = 9000)),
    "2" to listOf(Menu(name = "군만두", price = 8000), Menu(name = "물만두", price = 7000))
)
  • mapOf를 사용하여 불변 맵 map을 생성합니다.
  • 각 키에 대해 List 형태의 값을 저장하여, 여러 메뉴를 키 하나에 묶을 수 있습니다.
    • "1" 키는 짜장면과 짬뽕 리스트를 가지고 있으며,
    • "2" 키는 군만두와 물만두 리스트를 가지고 있습니다.

전체 코드의 요약

1. Menu 데이터 클래스: name과 price 속성을 가진 메뉴를 정의합니다.
2. 불변 리스트 menus: listOf를 사용하여 메뉴 목록을 생성하고 순회합니다.
3. Iterator 사용: menus 리스트의 Iterator를 생성하여 각 요소를 출력합니다.
4. 가변 리스트 board: mutableListOf를 사용하여 메뉴 항목을 추가하고, 이를 출력합니다.
5. 가변 맵 board2: mutableMapOf를 사용하여 키-값 형태의 메뉴 맵을 생성하고, 특정 키와 모든 키에 해당하는 메뉴를 출력합니다.
6. 중첩 리스트를 가진 맵 map: mapOf를 사용하여 키에 여러 개의 메뉴를 담은 리스트를 저장합니다.

이 코드는 Kotlin의 다양한 컬렉션 타입(List, MutableList, Map, MutableMap)을 사용하여 데이터를 관리하고 순회하는 방법을 보여줍니다.

profile
Back-end Developer를 목표로 하고 있는 전진구입니다.

0개의 댓글