ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 11-1-2. 도메인 특화 언어 : 연산자 오버로딩(5~8)
    Study(종료)/Kotlin 22.09.13 ~ 12.18 2022. 11. 25. 16:41

    11장

    11-1-1. 도메인 특화 언어 : 연산자 오버로딩(1~5)

    11-1-2.  도메인 특화 언어 : 연산자 오버로딩(6~8)

    11-2.  도메인 특화 언어 : 위임 프로퍼티

    11-3. 도메인 특화 언어 : 고차 함수와 DSL


    11-1-2. 도메인 특화 언어 : 연산자 오버로딩(6~8)

     

    6) 호출과 인덱스로 원소 찾기

    호출 관습(convention)을 사용해 값을 함수처럼 호출 식에서 사용할 수 있다.

    사용 방법은 필요한 파라미터와 함께 invoke() 함수를 정의하면 된다.

    함수 타입의 값은 자동으로 invoke()함수가 생기지만

    원한다면 임의의 타입에 대해 invoke() 함수를 정의할 수 있다.

    operator fun <K, V> Map <K, V>.invoke(key: K) = get(key)
    fun main() {
        val map = mapOf("I" to 1, "V" to 5, "X" to 10)
        println(map("V")) // 5
        // invoke() 가 없을 경우 컴파일 에러
        // Expression 'map' of type 'Map<String, Int>' cannot be invoked as a function. 
        // The function 'invoke()' is not found
        println(map("L")) // null
    }

    문자열, 배열, 리스트 등의 여러 객체에 대해 인덱스 연산 [ ]를 적용하는 것도 가능하다.

    인덱스 연산자 호출에 대한 내부적인 번역은,

    인덱스 연산자 식이 값으로 쓰이는지 / 대입의 왼쪽에 있는지에 따라 달라진다.

     

    전자의 경우 컴파일러는 데이터 읽기 접근을 가정하므로,

    인덱스 연산자를 get()  함수 호출로 변환하고 인자에 인덱스를 넣어준다.

    val array = arrayOf(1, 2, 3)
    println(array[0])
    println(array.get(0)) // 동일한 코드

    후자의 경우 컴파일러는 이를 set() 함수 호출로 만들어준다.

    이 경우 인덱스를 첫 번째 인자로, 설정하려는 값을 두 번째 인자로 set()에 전달한다.

     

    val array = arrayOf(1, 2, 3)
    array[0] = 10
    array.set(0, 10) // 동일한 코드

    인덱스가 꼭 정수일 필요 없이, 어떤 타입의 값이어도 상관없다.

    예를들면 맵의 인덱스 연산은 키값을 인자로 받는다.

    TreeNode 클래스 자식에 접근 가능한 get(), set() 연산자를

    추가하는 코드를 작성하고 결과를 보면 아래와 같다.

    class TreeNode<T>(val data: T){
        private val _children = arrayListOf<TreeNode<T>>()
        var parent: TreeNode<T>? = null
            private set
        operator fun plusAssign(data: T) {
            val node = TreeNode(data)
            _children += node
            node.parent = this
        }
        operator fun minusAssign(data: T){
            val index = _children.indexOfFirst { it.data == data }
            if(index > 0) return
            val node = _children.removeAt(index)
            node.parent = null
        }
        operator fun get(index: Int) = _children[index]
    
        operator fun set(index: Int, node: TreeNode<T>){
            node.parent?._children?.remove(node)
            node.parent = this
            _children[index].parent = null
            _children[index] = node
        }
    
    }
    
    fun main() {
    
        val root = TreeNode("Root")
        root += "Child 1"
        root += "Child 2"
        root[0] = TreeNode("Child 0")
        root += "Child 3"
        println(root.data)
        for(i in 0..2){
            println(root[i].data)
        }
        // Root
        // Child 0
        // Child 2
        // Child 3
    }

     


    7) 구조 분해

    연산자 오버로딩을 사용하면 임의의 타입에 대해 구조 분해를 제공할 수 있다.

    해야 할 일은 파라미터가 없는 componetN() 이라는 이름의 컴포넌트 함수를 멤버 함수나 확장 함수로 정의하는 것 이다. 여기서 N은 1부터 시작하는 정수이며, 이 함수를 정의하고 나면 구조 분해 선언의 각 변수는 각각의 순서에 따라 (1부터 정해지는) componentN() 함수의 반환값과 타입으로 정해진다.


    8) 이터레이션

    원하는 타입에 대해 iterator() 함수를 멤버나 확장으로 정의하면 for 루프를 사용할 수 있다.

    앞에서 본 TreeNode 클래스에 대해 이터레이션을 지원해보고 싶다면,

    operator fun <T>TreeNode<T>.iterator() = children.iterator()

    위 코드를 추가하면 children 멤버를 명시적으로 참조하지 않아도 TreeNode 인스턴스를

    for 루프에 사용해 자식 노드를 다룰 수 있다.

    fun main() {
        val content = TreeNode("Title").apply {
            addChild("Topic 1").apply {
                addChild("Topic 1.1")
                addChild("Topic 1.2")
            }
            addChild("Topic 2")
            addChild("Topic 3")
        }
    
        for (item in content){
            println(item.data)
            // Topic 1
            // Topic 2
            // Topic 3
        }
    }

    다음글

    11-2.  도메인 특화 언어 : 위임 프로퍼티

    반응형

    댓글

Designed by Tistory.