[kotlin] 코틀린을 다루는 기술 — 4장 재귀, 공재귀, 메모화

2021. 6. 21. 14:42모바일/Android_Kotlin

코틀린을 다루는 기술 — 4장 재귀, 공재귀, 메모화

concat

  • concat을 사용하면 기존의 리스트에 새로운 리스트를 추가하여 새 리스트를 반환한다
  • 끝에 NIL포인터를 새 리스트로 연결시킨다

나머지 설명

  • LIst 클래스에는 2개의 비공개 하위 클래스가 있다. 한 가지는 빈 리스트를 표현하는 Nil이고, 다른 하나는 비어있지 않은 리스트를 표현하는 Cons이다.
  • 자세한 설명은 주석에 달아놓았다.
fun main() {
//    println(add(2))
//    val squareOfTriple = compose(::square,::tripe)
//    var  lit :List<Char> = arrayListOf('d','d')
//    print(toString(lit,"string"))
    val a :MyList<Int> = MyList(1,2,3)
    //위 코드는 List의 생성자를 호출하는게 아닌, companio object의 invoke 함수를 호출한다.
    val b = a.concat(MyList.invoke(1,2,3,24,2))


    println(a)
    //[1, 2, 3, NIL]
    println(b)
    //[1, 2, 3, 1, 2, 3, 24, 2, NIL]


}
open class Pair<A,B>(val first:A,val second:B)
//class MyList<A>(private val head : A, val tail:MyList<A>):Pair<A,MyList<A>>(head,tail)

sealed class MyList<A> {

    abstract fun isEmpty(): Boolean

    abstract fun setHead(a: A): MyList<A>

    fun cons(a: A): MyList<A> = Cons(a, this)

    fun drop(n: Int): MyList<A> = drop(this, n)

    fun dropWhile(p: (A) -> Boolean): MyList<A> = dropWhile(this, p)

    fun concat(list: MyList<A>): MyList<A> = concat(this, list)

    fun init(): MyList<A> = TODO("init")

    fun reverse(): MyList<A> = TODO("reverse")

    internal object Nil: MyList<Nothing>() {

        override fun setHead(a: Nothing): MyList<Nothing> = throw IllegalStateException("setHead called on an empty list")

        override fun isEmpty() = true

        override fun toString(): String = "[NIL]"
    }

    internal class Cons<A>(val head: A, val tail: MyList<A>): MyList<A>() {
        //A(머리) , MyList<A> 꼬리를 인자로 받는다.
        //두 인자를 내부 가시성으로 선언해 List 클래스가 선언된 파일 안에서만 볼수 있게 한다.
        //Nil과 Cons를 비공개 가시성으로 선언해 항상 동반 객체의 invoke함수를 통해서만 리스트를 만들게 강제한다.
        override fun setHead(a: A): MyList<A> = tail.cons(a)

        override fun isEmpty() = false

        override fun toString(): String = "[${toString("", this)}NIL]"

        private tailrec fun toString(acc: String, list: MyList<A>): String = when (list) {
            Nil  -> acc
            is Cons -> toString("$acc${list.head}, ", list.tail)
        }
    }

    companion object {

        tailrec fun <A> drop(list: MyList<A>, n: Int): MyList<A> = when (list) {
            Nil -> list
            is Cons -> if (n <= 0) list else drop(list.tail, n - 1)
        }

        tailrec fun <A> dropWhile(list: MyList<A>, p: (A) -> Boolean): MyList<A> = when (list) {
            Nil -> list
            is Cons -> if (p(list.head)) dropWhile(list.tail, p) else list
        }

        fun <A> concat(list1: MyList<A>, list2: MyList<A>): MyList<A> = when (list1) {
            Nil -> list2
            is Cons -> concat(list1.tail, list2).cons(list1.head)
        }

        tailrec fun <A> reverse(acc: MyList<A>, list: MyList<A>): MyList<A> = when (list) {
            Nil -> acc
            is Cons -> reverse(acc.cons(list.head), list.tail)
        }

        operator fun <A> invoke(vararg az: A): MyList<A> =
            az.foldRight(Nil as MyList<A>) { a: A, list: MyList<A> -> Cons(a, list) }
    }
}