ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • kotlin) 코틀린에서의 FP
    kotlin 2024. 7. 9. 23:04

    코틀린에서 배열과 컬렉션을 다루는 방법

    1. 배열

    //Java 
    int[] array= {100, 200};
    
    for(int i = 0; i < array.length; i++) {
      System.out.println(array[i]);
    }
    
    //Kotlin
    val array = arrayOf(100,200)
    array.plus(300)		//값을 쉽게 넣을 수 있다.
    
    //1. array.indices : Index 만 가져옴
    for(i in array.indices) {
        println(array[i])
    }
    
    //2. withIndex : Index 와 value 를 한번에 받음
    for((idx, value) in array.withIndex()) {
        println(idx)
        println(value)
    }

    2. 코틀린에서의 Collection - List, Set, Map

    컬렉션을 만들어줄 때 불변인지, 가변인지를 설정해야 한다.

    가변 컬렉션 : 컬렉션에 element 를 추가 삭제할 수 있다.

    불변 컬렉션 : 컬렉션에 element 를 추가 삭제할 수 없다.

    * element : 콜렉션 내부에 저장되는 각각의 개별 항목


    코틀린에서 람다를 다루는 방법

    1. Java에서 람다를 다루기 위한 노력

    기존 Java 의 문제점

    1. '익명 클래스' 를 사용하는것은 복잡하다.
    2. 다양한 Filter 가 필요할 수 있다.

    -> JDK8 부터 람다 (이름이 없는 함수) 등장

     

    람다

    1. 변수 -> 변수를 이용한 함수
    2. (변수1, 변수2) -> 변수1과 변수 2를 이용한 함수
    3. 간결한 스트림 등장

    기존 Java 에서 함수 특징

    1. 메소드 자체를 직접 넘겨주는 것처럼 쓸 수 있다.
    2. Java 에서는 함수는 변수에 할당되거나 파라미터로 전달할 수 없다.

     

    2. 코틀린에서의 람다

    Java 와 다른 Kotlin 만의 특징 : 코틀린에서는 함수가 그 자체로 값이 될 수 있다. 변수에 할당할수도, 파라미터로 넘길 수도 있다.

    fun main() {
        var fruits = listOf(
            Fruit("사과", 1000),
            Fruit("사과", 1200),
            Fruit("사과", 1200),
            Fruit("사과", 1500),
            Fruit("바나나", 3000),
            Fruit("바나나", 3200),
            Fruit("바나나", 2500),
            Fruit("수박", 10000)
        )
    
    
        //원래 fun 함수이름(fruit: Fruit) 이 되어야하지만 이름이 없기에 이름없는 함수(람다) 가 된다.
        var isApple = fun(fruit: Fruit): Boolean {
            return fruit.name == "사과"
        }
        
        var isApple2 = {fruit: Fruit -> fruit.name == "사과"}
        
        isApple(fruits[0])
        isApple.invoke(fruits[0])
    }
    //Java
    private List<Fruit> filterFruits(List<Fruit> fruits, Predicate<Fruit> fruitFilter) {
      List<Fruit> results = new ArrayList<>();
      for (Fruit fruit : fruits) {
        if (fruitFilter.test(fruit)) {
          results.add(fruit);
        }
      }
      return results;
    }
    
    fun main() {
        var fruits = listOf(
            Fruit("사과", 1000),
            Fruit("사과", 1200),
            Fruit("사과", 1200),
            Fruit("사과", 1500),
            Fruit("바나나", 3000),
            Fruit("바나나", 3200),
            Fruit("바나나", 2500),
            Fruit("수박", 10000)
        )
    
    
        //원래 fun 함수이름(fruit: Fruit) 이 되어야하지만 이름이 없기에 이름없는 함수(람다) 가 된다.
        var isApple = fun(fruit: Fruit): Boolean {
            return fruit.name == "사과"
        }
        var isApple2 = {fruit: Fruit -> fruit.name == "사과"}
        filterFruits(fruits, isApple)       //직접적으로 함수 사용 가능
        filterFruits(fruits, isApple2)
        filterFruits(fruits, {fruit: Fruit -> fruit.name == "사과" })
        filterFruits(fruits) {it.name == "사과"} //극단적으로 생략이 가능하다, it은 파라미터가 1개일 경우 사용 가능하다.
    }
    //Kotlin
    private fun filterFruits(fruits: List<Fruit>, filter: (Fruit) -> Boolean): List<Fruit> {
        val results = mutableListOf<Fruit>()
        for (fruit in fruits) {
            if (filter(fruit)) {
                results.add(fruit)
            }
        }
        return results
    }

    함수의 인터페이스를 쓰는게 아니라 함수 자체를 파라미터로 받아서 사용할 수 있다.

    3. Closure

    코틀린에서는 람다가 시작하는 시점에 참조하고 있는 변수들을 모두 포획하여 그 정보를 가지고 있다.

    //Java
    String targetFruitName = "바나나";
    targetFruitName = "수박";
    
    //Java 에서는 람다를 쓸 때 사용할 수 있는 변수에 제약이 있다.
    filterFruits(fruits, (fruit -> targetFruitName.equals(fruit.getName())));   //에러 발생
    
    
    //Kotlin
    var targetFruitName = "바나나"
    targetFruitName = "수박"
    //Kotlin 에서는 문제없이 돌아간다.
    filterFruits(fruits) {it.name == targetFruitName}

    이렇게 해야만, 람다를 진정한 일급 시민으로 간주할 수 있다.

    이 데이터 구조를 Closure 라고 한다.


    코틀린에서 컬렉션을 함수형으로 다루는 방법

    1. 필터와 맵

    fun main() {
        var fruits = listOf(
            Fruit("사과", 1000),
            Fruit("사과", 1200),
            Fruit("사과", 1200),
            Fruit("사과", 1500),
            Fruit("바나나", 3000),
            Fruit("바나나", 3200),
            Fruit("바나나", 2500),
            Fruit("수박", 10000)
        )
    
        //사과만 주세요
        //방법 1 : 단순 검색
        val apples1 = fruits.filter { fruit -> fruit.name == "사과"}
        //방법 2 : 인덱스까지
        val apples2 = fruits.filterIndexed{ index, fruit ->
            println(index)
            fruit.name == "사과"
        }
    
        //Map 을 통한 가격 검색
        //방법 1 : 사과 중에서 가격
        val applePrices1 = fruits.filter {fruit -> fruit.name == "사과" }
            .map {fruit -> fruit.price}
        //방법 2 : 인덱스 까지
        val applePrices2 = fruits.filter {fruit -> fruit.name == "사과" }
            .mapIndexed { index, fruit ->
                println(index)
                fruit.price
            }
        //방법 3 : null 제외
        val applePriceNotNull = fruits.filter {fruit -> fruit.name == "사과" }
            .mapNotNull { fruit -> fruit.price }
    }

    2. 다양한 컬렉션 처리 기능

    //all : 조건을 모두 만족하면 true 그렇지 않으면 false
    val isAllApple = fruits.all {fruit -> fruit.name == "사과"}
    //none : 조건을 모두 불만족하면 true 그렇지 않으면 false
    val isNoApple = fruits.none {fruit -> fruit.name == "사과"}
    //any : 조건을 하나라도 만족하면 true 그렇지 않으면 false
    val isAnyApple = fruits.any{fruit -> fruit.price >= 10000}

    3. List 를 Map 으로

    //과일 이름이 Key 이고 이름을 토대로 Map 을 만들고싶을때
    val fruitNameMap : Map<String, List<Fruit>> = fruits.groupBy { fruit ->  fruit.name }
    val fruitIdMap : Map<Long, Fruit> = fruits.associate { fruit -> fruit.id }
    
    //List<List<Fruit>> 를 List<Fruit> 로 바꿀때
    fruiltInList.fatten()

    댓글

Designed by Tistory.