Skip to content

Instantly share code, notes, and snippets.

@zion830
Last active July 11, 2020 02:29
Show Gist options
  • Save zion830/e37efceb86f3e8f7f9c0700de45f3008 to your computer and use it in GitHub Desktop.
Save zion830/e37efceb86f3e8f7f9c0700de45f3008 to your computer and use it in GitHub Desktop.

4์žฅ ์žฌ๊ท€, ๊ณต์žฌ๊ท€, ๋ฉ”๋ชจํ™”

  • ์žฌ๊ท€, ๊ณต์žฌ๊ท€, ๊ผฌ๋ฆฌ ์žฌ๊ท€ ํ•จ์ˆ˜์˜ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณธ๋‹ค.
  • TCE๋ฅผ ์‚ฌ์šฉํ•ด ๊ผฌ๋ฆฌ ์žฌ๊ท€ ํ˜ธ์ถœ์„ ์ตœ์ ํ™”ํ•œ๋‹ค.
  • ๋žŒ๋‹ค๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋งŒ๋“ ๋‹ค.
  • memorization์„ ์ ์šฉํ•ด ๊ณ„์‚ฐ ์†๋„๋ฅผ ๋†’์ธ๋‹ค.

4.1 ๊ณต์žฌ๊ท€์™€ ์žฌ๊ท€

  • ์žฌ๊ท€(recursive) : ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„๋ถ€ํ„ฐ ๊ณ„์‚ฐ์„ ์‹œ์ž‘ํ•œ๋‹ค.
  • ๊ณต์žฌ๊ท€(corecursive) : ํ•œ ๋‹จ๊ณ„์˜ ์ถœ๋ ฅ์„ ๋‹ค์Œ ๋‹จ๊ณ„์˜ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ณ„์‚ฐ ๋‹จ๊ณ„๋ฅผ ํ•ฉ์„ฑํ•œ ๊ฒƒ์œผ๋กœ, ์ฒซ ๋‹จ๊ณ„๋ถ€ํ„ฐ ๊ณ„์‚ฐ์„ ์‹œ์ž‘ํ•œ๋‹ค.

์˜ˆ์ œ - ๋ฌธ์ž๋กœ ์ด๋ค„์ง„ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•˜๋‚˜์˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

  • ์ดˆ๊ธฐ ๋ฌธ์ž์—ด s๋กœ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด ํ•œ๋‹จ๊ณ„์”ฉ ๋ณ€ํ˜•
fun append(s: String, c: Char) = "$s$c"

fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
    s
} else {
		// toString(list.slice(1 until list.size), append(s, list[0]))
    toString(list.drop(1), append(s, list.first())) // ๊ฐ™์€ ํ‘œํ˜„
}

fun main(args: Array<String>) {
    println(toString(listOf('1', '2', '3'), "abc")) // abc123
}
  • List๋งŒ ์žˆ์–ด๋„ ๋˜๋„๋ก ๊ฐœ์„ ํ•˜๊ธฐ
    • ์ฝ”ํ‹€๋ฆฐ์€ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜์˜ ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์—†๋‹ค. ์•ˆ์ชฝ ํ•จ์ˆ˜๋Š” ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ผ ํƒ€์ž…์„ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค.
fun append(s: String, c: Char) = "$s$c"

fun toString(list: List<Char>): String {
    fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
        s
    } else {
        toString(list.drop(1), append(s, list.first()))
    }

    return toString(list, "")
}

fun main(args: Array<String>) {
    println(toString(listOf('1', '2', '3'))) // 123
}

๋งŒ์•ฝ append ๋Œ€์‹  ์•„๋ž˜์˜ ๋ฉ”์„œ๋“œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด?

fun prepend(s: String, c: Char) = "$c$s"
  1. ๊ณต์žฌ๊ท€๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ๋ฆฌ์ŠคํŠธ์˜ ์ฒซ๋ฒˆ์งธ ์š”์†Œ ๋Œ€์‹  ๋งˆ์ง€๋ง‰ ์š”์†Œ๋ฅผ ์ด์šฉํ•ด ๋‹จ๊ณ„๋ณ„๋กœ ์ง„ํ–‰ํ•จ

    ๋งŒ์•ฝ ๋ฆฌ์ŠคํŠธ ๋Œ€์‹  ๋‹จ์ผ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๋งค์šฐ ๋น„ํšจ์œจ์ 

    fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
    		s
    } else {
    		toString(list.subList(0, list.size - 1), prepend(s, list.last()))
    }
  2. ์žฌ๊ท€๋กœ ๊ตฌํ˜„ํ•  ๊ฒฝ์šฐ

    ์žฌ๊ท€๋Š” ์ตœ์ข… ์กฐ๊ฑด(list.isEmpty())์— ๋„๋‹ฌํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๊ณ„์‚ฐ์ด ๋ฏธ๋ค„์ง€๊ณ , ์ค‘๊ฐ„ ๊ฒฐ๊ณผ๋Š” stack ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋œ๋‹ค.

    fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
        s
    } else {
        prepend(toString(list.subList(1, list.size)), list.first())
    }

์žฌ๊ท€ ํ•จ์ˆ˜์™€ ๊ณต์žฌ๊ท€ ํ•จ์ˆ˜ ๊ตฌ๋ถ„

ํ•จ์ˆ˜๊ฐ€ ๊ณ„์‚ฐ์˜ ์ผ๋ถ€๋ถ„์œผ๋กœ ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด ๊ทธ ํ•จ์ˆ˜๋Š” ์žฌ๊ท€์ ์ด๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๋”๋ผ๋„ ์ง„์ •ํ•œ ์žฌ๊ท€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋‹ค.

์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ์žฌ๊ท€์ ์ธ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ–ˆ์ง€๋งŒ, ์‹ค์ œ๋กœ ์ด ํ•จ์ˆ˜๋Š” ๋ฌดํ•œ ๋ฃจํ”„๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณต์žฌ๊ท€ ํ•จ์ˆ˜๋‹ค.

fun hello() {
		hello()
}

๊ณต์žฌ๊ท€์™€ ์žฌ๊ท€์˜ ์ฐจ์ด์ 

  • ๊ณต์žฌ๊ท€์—์„œ๋Š” ๊ฐ ๋‹จ๊ณ„๋ฅผ ์ฆ‰์‹œ ๊ณ„์‚ฐํ•œ๋‹ค, ์žฌ๊ท€ ํ•จ์ˆ˜๋Š” ์ตœ์ข… ์กฐ๊ฑด์— ๋„๋‹ฌํ•˜๊ณ  ๋‚˜์„œ ์ด์ „ ๋‹จ๊ณ„๋ฅผ ์—ญ์ˆœ์œผ๋กœ ๊ณ„์‚ฐํ•œ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ์—” ํ˜„์žฌ ๊ณ„์‚ฐ์— ํ•„์š”ํ•œ ๊ฐ’๋งŒ ์ €์žฅ๋œ๋‹ค.
  • ์žฌ๊ท€์—์„œ๋Š” ์ตœ์ข… ์กฐ๊ฑด์— ๋„๋‹ฌํ•  ๋•Œ ๊นŒ์ง€ ๋ชจ๋“  ๋‹จ๊ณ„๋ฅผ ์–ด๋–ค ํ˜•ํƒœ๋กœ๋“  ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•ด์งˆ ํ™•๋ฅ ์ด ๋†’๊ณ , ์ด ๋ฌธ์ œ๋ฅผ ํ”ผํ•˜๋ ค๋ฉด ์žฌ๊ท€ ๋Œ€์‹  ๊ณต์žฌ๊ท€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ตœ์„ ์ด๋‹ค.

4.2 ๊ผฌ๋ฆฌ ํ˜ธ์ถœ ์ œ๊ฑฐ

์ค‘๊ฐ„ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๊ณต์žฌ๊ท€๋„ ๊ฒฐ๊ตญ์€ ์Šคํƒ ๊ณต๊ฐ„์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋Š๋ฆฌ๊ธด ํ•˜์ง€๋งŒ ์–ธ์  ๊ฐ€ ์Šคํƒ์„ ๊ณ ๊ฐˆ์‹œํ‚ฌ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ์™„์ „ํžˆ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด์„  ๊ณต์žฌ๊ท€๋ฅผ ์ผ๋ฐ˜์ ์ธ ๋ฃจํ”„๋กœ ๋ฐ”๊พธ๋ฉด ๋œ๋‹ค.

fun toString2(list: List<Char>): String {
    var s = ""
    for (c in list) {
        s = append(s, c)
    }
    return s
}

๊ผฌ๋ฆฌ ํ˜ธ์ถœ ์ œ๊ฑฐ (TCE, Tail Call Elimination)

์ฝ”ํ‹€๋ฆฐ์• ๋Š” ๊ณต์žฌ๊ท€๋ฅผ ์ž๋™์œผ๋กœ ๋ฃจํ”„๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

  • ๊ผฌ๋ฆฌ ์žฌ๊ท€ ํ˜ธ์ถœ : ์žฌ๊ท€ ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅธ ์—ฐ์‚ฐ์— ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜

    ์ž๊ธฐ ์ž์‹ ๋งŒ์„ ํ˜ธ์ถœํ•˜๋‹ค๊ฐ€ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ตฌ์กฐ๋กœ ๊ผฌ๋ฆฌ ์žฌ๊ท€ ํ•จ์ˆ˜ O

    fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
        s
    } else {
        toString(list.drop(1), append(s, list.first()))
    }

    ๊ฒฐ๊ณผ ๊ฐ’์— trim()์ด๋ผ๋Š” ์ถ”๊ฐ€์ ์ธ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ ๊ผฌ๋ฆฌ ์žฌ๊ท€ ํ•จ์ˆ˜ X

    fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
        s
    } else {
        toString(list.drop(1), append(s, list.first())).trim()
    }
  • ์ฝ”ํ‹€๋ฆฐ์—์„œ ํ•จ์ˆ˜ ์•ž์— tailrec ํ‚ค์›Œ๋“œ๋ฅผ ์•ž์— ๋ถ™์ด๋ฉด ๊ผฌ๋ฆฌ ํ˜ธ์ถœ์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค. ์žฌ๊ท€ ๋Œ€์‹  ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค.

  • tailrec์„ ๋ถ™์ด๊ณ  ๋””์ปดํŒŒ์ผ ํ•ด๋ณด๊ธฐ

    • Kotlin์œผ๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ
    tailrec fun toString(list: List<Char>, s: String): String = if (list.isEmpty()) {
        s
    } else {
        toString(list.drop(1), append(s, list.first()))
    }
    • ๋””์ปดํŒŒ์ผ ๊ฒฐ๊ณผ ์ฝ”ํ‹€๋ฆฐ์ด ์ด ํ•จ์ˆ˜๊ฐ€ ๊ผฌ๋ฆฌ ์žฌ๊ท€์ž„์„ ๊ฐ์ง€ํ•˜๊ณ  TCE๋ฅผ ์ ์šฉํ–ˆ๋‹ค.
    @NotNull
       public static final String toString(@NotNull List list, @NotNull String s) {
          while(true) {
             Intrinsics.checkParameterIsNotNull(list, "list");
             Intrinsics.checkParameterIsNotNull(s, "s");
             if (list.isEmpty()) {
                return s;
             }
    
             List var10000 = CollectionsKt.drop((Iterable)list, 1);
             s = append(s, (Character)CollectionsKt.first(list));
             list = var10000;
          }
       }

์žฌ๊ท€๋ฅผ ๊ณต์žฌ๊ท€๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

์žฌ๊ท€๊ฐ€ ๊ณต์žฌ๊ท€๋ณด๋‹ค ๋ฉ”๋ชจ๋ฆฌ ์ œ์•ฝ์ด ํฌ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์œผ๋‚˜, ์žฌ๊ท€์—๋Š” ์ฝ”๋“œ ์ž‘์„ฑ์ด ๋‹จ์ˆœํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

// 1..n๊นŒ์ง€์˜ ํ•ฉ์„ ๊ตฌํ•˜๋Š” ํ•จ์ˆ˜

// ์žฌ๊ท€๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ
fun sum(n: Int): Int = if (n < 1) 0 else n + sum(n - 1)

// ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ
fun sum(n: Int): Int {
    var result = 0
    for (i in 0..n) {
        result += n
    }
    return result
}

์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค s + i์™€ i + 1์˜ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๋„˜๊ฒจ์ฃผ๋ฉด ๊ณต์žฌ๊ท€๋กœ ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์•ž์— tailrec์„ ๋ถ™์—ฌ TCE๋ฅผ ์ ์šฉํ•˜๋ฉด ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋™์ž‘์ด ๋œ๋‹ค.

fun sum(n: Int): Int {
    tailrec fun sum(s: Int, index: Int): Int =
        if (index > n) s else sum(s + index, index + 1)
    return sum(0, 0)
}
  • ์—ฐ์Šต ๋ฌธ์ œ 4.1

    ์–‘์˜ ์ •์ˆ˜์— ๋Œ€ํ•ด ์ž‘๋™ํ•˜๋Š” ๊ณต์žฌ๊ท€ add ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๋ผ. add ๊ตฌํ˜„์—๋Š” +๋‚˜ -๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๋‹ค์Œ ๋‘ ํ•จ์ˆ˜๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

    fun inc(n: Int) = n + 1
    fun dec(n: Int) = n - 1
    • loop๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ
    fun add(a: Int, b: Int): Int {
        var x = a
        var index = b
        while (index > 0) {
            x = inc(x)
            index = dec(index)
        }
        return x
    }
    • ๊ณต์žฌ๊ท€๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ
    tailrec fun add(a: Int, index: Int): Int = if (index > 0) {
        add(inc(a), dec(index))
    } else {
        a
    }
  • ์—ฐ์Šต ๋ฌธ์ œ 4.2

    ์žฌ๊ท€์  ๊ณ„์Šน ํ•จ์ˆ˜ ๊ฐ’์„ ์ž‘์„ฑํ•˜๋ผ. ํ•จ์ˆ˜ ๊ฐ’์€ val ํ‚ค์›Œ๋“œ๋กœ ์ •์˜๋œ ํ•จ์ˆ˜๋‹ค.

    • ์ง€์—ฐ์ดˆ๊ธฐํ™”๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋žŒ๋‹ค์‹ ๋‚ด๋ถ€์—์„œ factorial์„ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์˜ค๋ฅ˜๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ factorial์„ ์ •์˜ํ•˜๋˜ ์ค‘๊ฐ„์— ์•„์ง ์ •์˜๊ฐ€ ๋๋‚˜์ง€ ์•Š์€ factorial์„ ํ˜ธ์ถœํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์ง€์—ฐ์ดˆ๊ธฐํ™”๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
    val factorial: (Int) -> Int by lazy {
        { n: Int -> if (n <= 1) n else n * factorial(n - 1) }
    }

    13๋ถ€ํ„ฐ๋Š” int๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„๋ฅผ ๋„˜์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ์ œ๋Œ€๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ๋งŒ์•ฝ ์ œ๋Œ€๋กœ ๋œ ๊ฐ’์„ ์›ํ•œ๋‹ค๋ฉด Int ๋Œ€์‹  BigInteger๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

4.3 ์žฌ๊ท€ ํ•จ์ˆ˜์™€ ๋ฆฌ์ŠคํŠธ

๋ฆฌ์ŠคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌ์ŠคํŠธ๋ฅผ 0๋ฒˆ ์š”์†Œ์™€ 0๋ฒˆ ์š”์†Œ๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ๋‘ ๋ถ€๋ถ„์œผ๋กœ ์ชผ๊ฐœ์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค. 0๋ฒˆ ์š”์†Œ๋ฅผ head, ๋‚˜๋จธ์ง€๋ฅผ tail์ด๋ผ ๋ถ€๋ฅธ๋‹ค.

๋ฆฌ์ŠคํŠธ์˜ ๋จธ๋ฆฌ์™€ ๊ผฌ๋ฆฌ๋ฅผ ๋Œ๋ ค์ฃผ๋Š” ๋„์šฐ๋ฏธ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜์ž.

fun <T> List<T>.head(): T = if (isEmpty()) {
    throw IllegalArgumentException("head called on empty list")
} else {
    this[0]
}

fun <T> List<T>.tail(): List<T> = if (isEmpty()) {
    throw IllegalArgumentException("tail called on empty list")
} else {
    drop(1)
}
  • ์•„๊นŒ ์ „ ์ž‘์„ฑํ–ˆ๋˜ toString()์— ํ™•์žฅ ํ•จ์ˆ˜ head(), tail()๋ฅผ ์ ์šฉํ•ด์„œ ๋” ์ง๊ด€์ ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
fun toString(list: List<Char>): String {
    tailrec fun toString_(list: List<Char>, s: String): String =
        if (list.isEmpty()) s else toString_(list.tail(), append(s, list.head()))

    return toString_(list, "")
}

์ธ์ž ํƒ€์ž…์ด ๊ฐ™์•„๋„ ์ถฉ๋Œ์ด ๋‚˜์ง€ ์•Š๋„๋ก, ์•ž์œผ๋กœ๋Š” ๋„์šฐ๋ฏธ ํ•จ์ˆ˜๋Š” ์ฃผ ํ•จ์ˆ˜_ ํ˜•์‹์œผ๋กœ ๋„ค์ด๋ฐ ํ•˜๊ฒ ๋‹ค.

์ด์ค‘ ์žฌ๊ท€ ํ•จ์ˆ˜(doubly recursive function)

์ด์ค‘ ์žฌ๊ท€๋Š” ๋‹จ๊ณ„๋งˆ๋‹ค ์ž์‹ ์„ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

  • ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด์€ ์ด์ค‘ ์žฌ๊ท€ ํ•จ์ˆ˜๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋‹ค.

    fun fibonacci(n: Int): Int = if (n <= 1) 1 else fibonacci(n - 1) + fibonacci(n - 2)

    ํ•œ ๋ฒˆ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค 2๊ฐœ์˜ ์ถ”๊ฐ€ ํ˜ธ์ถœ์ด ์ƒ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— f(n)์„ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด 2^n๊ฐœ์˜ ์žฌ๊ท€ ํ˜ธ์ถœ์ด ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋กœ์ธํ•ด ์ˆซ์ž๊ฐ€ ์กฐ๊ธˆ๋งŒ ์ปค์ ธ๋„ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๋ฌด์ง€๋ง‰์ง€ํ•˜๊ฒŒ ๊ธธ์–ด์ง„๋‹ค.

  • 4.3 ๊ผฌ๋ฆฌ ์žฌ๊ท€ ๋ฒ„์ „์˜ ํ”ผ๋ณด๋‚˜์น˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๋ผ.

    return ๋ฌธ์—์„œ + ์—ฐ์‚ฐ์„ ์ง์ ‘ ์ ์šฉํ•˜๋ฉด TCE ์ ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ f(n - 1)๊ณผ f(n - 2)์˜ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ˆ„์ ํ•˜๋Š” ์ธ์ž๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

    fun fibonacci(x: Int): BigInteger {
        tailrec fun fib(n1: BigInteger, n2: BigInteger, x: BigInteger): BigInteger =
            when {
                (x == BigInteger.ZERO) -> BigInteger.ONE
                (x == BigInteger.ONE) -> n1 + n2
                else -> fib(n2, n1 + n2, x - BigInteger.ONE)
            }
        return fib(BigInteger.ZERO, BigInteger.ONE, BigInteger.valueOf(x.toLong()))
    }
  • 4.4 makeString์˜ ๊ผฌ๋ฆฌ ์žฌ๊ท€ ๋ฒ„์ „์„ ๋งŒ๋“ค์–ด๋ผ

    fun <T> makeString(list: List<T>, delim: String): String {
        tailrec fun makeString_(list: List<T>, acc: String): String = when {
            list.isEmpty() -> acc
            acc.isEmpty() -> makeString_(list.tail(), "${list.head()}")
            else -> makeString_(list.tail(), "$acc$delim${list.head()}")
        }
        return makeString_(list, "")
    }
  • 4.5 sum, toString, makeString์„ ์ •์˜ํ•  ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ผฌ๋ฆฌ ์žฌ๊ท€ ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ผ.

    fun <T, U> foldLeft(list: List<T>, z: U, f: (U, T) -> U): U {
        tailrec fun foldLeft(list: List<T>, acc: U): U = if (list.isEmpty()) {
                acc
            } else {
                foldLeft(list.tail(), f(acc, list.head()))
            }
        return foldLeft(list, z)
    }
  • 4.6 forLeft์™€ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ, ๊ณต์žฌ๊ท€ ๋Œ€์‹  ์žฌ๊ท€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” foldRight๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ

    fun <T, U> foldRight(list: List<T>, identity: U, f: (T, U) -> U): U =
        if (list.isEmpty()) {
            identity
        } else {
            f(list.head(), foldRight(list.tail(), identity, f))
    		}
    
    fun string(list: List<Char>): String = foldRight(list, "") { c, s -> prepend(c, s) }
  • 4.7 fold๋ฅผ ์‚ฌ์šฉํ•ด reverse ํ•จ์ˆ˜ ์ •์˜ํ•˜๊ธฐ

    fun <T> reverse(list: List<T>): List<T> =
        foldLeft(list, listOf(), { newList: List<T>, elem: T -> listOf(elem) + newList })
    
    fun main(args: Array<String>) {
        println(reverse(listOf(1, 2, 3)).joinToString()) // 3, 2, 1 
    }
  • 4.8 ๋‘ ๋ฆฌ์ŠคํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๊ณ , ๋ฆฌ์ŠคํŠธ + ์›์†Œ๋ฅผ ์‚ฌ์šฉํ•ด reverse ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ธฐ

    fun <T> prepend(list: List<T>, elem: T): List<T> =
        foldLeft(list, listOf(elem), { newList: List<T>, elm: T -> newList + elm })
    
    fun <T> reverse(list: List<T>): List<T> = foldLeft(list, listOf(), ::prepend)
  • 4.9 ์‹œ์ž‘, ๋, x โ†’ x+1๋กœ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜์˜ ๋ฃจํ”„ ๊ธฐ๋ฐ˜ ๊ตฌํ˜„ ์ž‘์„ฑ

    fun range(start: Int, end: Int): List<Int> {
        val result: MutableList<Int> = mutableListOf()
        var index = start
        while (index < end) {
            result.add(index)
            index++
        }
        return result
    }
  • 4.10 ์ž„์˜์˜ ํƒ€์ž…๊ณผ ์กฐ๊ฑด์— ๋Œ€ํ•ด์„œ๋งŒ ์ž‘๋™ํ•˜๊ฒŒ 4.9 ์ˆ˜์ •

    fun <T> unfold(seed: T, f: (T) -> T, p: (T) -> Boolean): List<T> {
        val result: MutableList<T> = mutableListOf()
        var elem = seed
        while (p(elem)) {
            result.add(elem)
            elem = f(elem)
        }
        return result
    }
  • 4.11 4.10์œผ๋กœ 4.9๋ฅผ ๊ตฌํ˜„

    fun range(start: Int, end: Int): List<Int> =
                    unfold(start, { it + 1 }, { it < end })
  • 4.12 4.9์˜ ์žฌ๊ท€ ๋ฒ„์ „์„ ์ž‘์„ฑ

    fun range(start: Int, end: Int): List<Int> =
        if (end <= start)
            listOf()
        else
            prepend(range(start + 1, end), start)
  • 4.13 4.10์˜ ์žฌ๊ท€ ๋ฒ„์ „์„ ์ž‘์„ฑ

    fun <T> unfold(seed: T, f: (T) -> T, p: (T) -> Boolean): List<T> =
            if (p(seed))
                prepend(unfold(f(seed), f, p), seed)
            else
                listOf()
  • 4.14 4.10์˜ ๊ผฌ๋ฆฌ์žฌ๊ท€ ๋ฒ„์ „ ์ž‘์„ฑ

    fun <T> unfold(seed: T, f: (T) -> T, p: (T) -> Boolean): List<T> {
        tailrec fun unfold_(acc: List<T>, seed: T): List<T> =
                if (p(seed))
                    unfold_(acc + seed, f(seed))
                else
                    acc
        return unfold_(listOf(), seed)
    }

4.4 ๋ฉ”๋ชจํ™”(memorization)

๋ฉ”๋ชจํ™”(memorization)๋Š” ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด ๋‚˜์ค‘์— ๊ฐ™์€ ๊ณ„์‚ฐ์„ ๋‹ค์‹œ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค. ์–ด๋–ค ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ํ•„์š”ํ•  ๋•Œ ๊ณ„์‚ฐ์„ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฉ”๋ชจํ™” ์ ์šฉ

fun fibonacciLoop(n: Int): Int {
    var first = 1
    var second = 1
    return when (n) {
        1 -> first
        2 -> second
        else -> {
            var current = first + second
            for (num in 3..n) {
                current = first + second
                first = second // f(n - 1)์„ ์ €์žฅ
                second = current // f(n)์„ ์ €์žฅ
            }
            current
        }
    }
}

์ด๋Ÿฐ ๊ธฐ๋ฒ•์€ ์•ˆ์ „ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™์„ ์œ„๋ฐฐํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ๋„ ๋ณด์ธ๋‹ค. ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจํ™”ํ•˜๋ ค๋ฉด ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด ์ƒํƒœ๊ฐ€ ๋ถ€์ˆ˜ ํšจ๊ณผ์— ํ•ด๋‹นํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ™์€ ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ญ์ƒ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋ฏ€๋กœ, ์•ˆ์ „ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™์„ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์žฌ๊ท€ํ•จ์ˆ˜์— ๋ฉ”๋ชจํ™” ์ ์šฉ

fun fibo(number: Int): String {
    tailrec fun fibo_(
        acc: List<BigInteger>,
        acc1: BigInteger,
        acc2: BigInteger,
        x: BigInteger
    ): List<BigInteger> =
        when (x) {
            BigInteger.ZERO -> acc
            BigInteger.ONE -> acc + (acc1 + acc2)
            else -> fibo_(acc + (acc1 + acc2), acc2, acc1 + acc2, x - BigInteger.ONE)
        }

    val list = fibo_(listOf(), BigInteger.ONE, BigInteger.ZERO, BigInteger.valueOf(number.toLong()))
    return list.joinToString()
}

์•”์‹œ์  ๋ฉ”๋ชจํ™” ์‚ฌ์šฉํ•˜๊ธฐ

ํ”ผ๋ณด๋‚˜์น˜ ์ˆ˜์—ด 1, 1, 2, 3, 5, 8...์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŠœํ”Œ์˜ ์ง‘ํ•ฉ์œผ๋กœ๋„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

(1, 1), (2, 3), (5, 8)... ํŠœํ”Œ๋กœ๋ถ€ํ„ฐ ๋‹ค์Œ ํŠœํ”Œ์„ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

val fibo = { (a, b): Pair<BigInteger, BigInteger> -> Pair(b, a + b)}
  • 4.16 unfold์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋Š” iterate ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๋ผ

    fun <T> iterate(seed: T, f: (T) -> T, n: Int): List<T> {
        tailrec fun iterate_(acc: List<T>, seed: T): List<T> =
            if (acc.size < n) {
                iterate_(acc + seed, f(seed))
            } else {
                acc
            }
        return iterate_(listOf(), seed)
    }
  • 4.17 T โ†’ U ํƒ€์ž… ํ•จ์ˆ˜๋ฅผ List ํƒ€์ž… ๋ฆฌ์ŠคํŠธ์˜ ๋ชจ๋“  ์›์†Œ์— ์ ์šฉํ•ด ๋งŒ๋“  List ํƒ€์ž…์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋Œ๋ ค์ฃผ๋Š” map ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค.

    fun <T, U> map(list: List<T>, f: (T) -> U): List<U> {
        tailrec fun map_(acc: List<U>, list: List<T>): List<U> =
            if (list.isEmpty()) {
                acc
            } else {
                map_(acc + f(list.head()), list.tail())
            }
        return map_(listOf(), list)
    }

์ž๋™ ๋ฉ”๋ชจํ™” ์‚ฌ์šฉํ•˜๊ธฐ

ํ•œ ๋ฒˆ ๊ณ„์‚ฐํ•œ ์ ์ด ์žˆ๋Š” ๊ฐ’์ด๋ผ๋ฉด, ์บ์‹œ์— ์ €์žฅํ•ด ๋’€๋‹ค๊ฐ€ ๋˜‘๊ฐ™์€ ๊ณ„์‚ฐ์ด ๋ฐ˜๋ณต๋  ๋•Œ ๊บผ๋‚ด ์“ฐ๋ฉด ๋จ

์ž…๋ ฅ์˜ ๊ฐ€์ง“์ˆ˜๊ฐ€ ๋งŽ์ง€ ์•Š๋‹ค๋ฉด ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด๋„ ๋˜๋‚˜, ๋งŒ์•ฝ ๊ฐ€์ง“์ˆ˜๊ฐ€ ๋งŽ๋‹ค๋ฉด ์†Œํ”„ํŠธ ์ฐธ์กฐ๋‚˜ ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

class Memoizer<T, U> private constructor() {

    private val cache = ConcurrentHashMap<T, U>()

    private fun doMemoize(function: (T) -> U):  (T) -> U = { input ->
            cache.computeIfAbsent(input) {ย ย ย ย 
                function(it)
            }
        }

    companion object {

        fun <T, U> memoize(function: (T) -> U): (T) -> U =ย ย ย ย 
            Memoizer<T, U>().doMemoize(function)
    }
}
fun longComputation(number: Int): Int {
    println("๊ณ„์‚ฐ ์‹คํ–‰")
    return number
}

fun main(args: Array<String>) {
    val memoizedLongComputation = Memoizer.memoize(::longComputation)
    val result1 = memoizedLongComputation(43)
    val result2 = memoizedLongComputation(43)
}

// ์‹คํ–‰ ๊ฒฐ๊ณผ "๊ณ„์‚ฐ ์‹คํ–‰"์ด 1ํšŒ ์ถœ๋ ฅ๋œ๋‹ค.

๋‹ค์ธ์ž ํ•จ์ˆ˜์˜ ๋ฉ”๋ชจํ™” ๊ตฌํ˜„ํ•˜๊ธฐ

3์žฅ์—์„œ ์–˜๊ธฐํ–ˆ๋“ฏ ๋‹ค์ธ์ž ํ•จ์ˆ˜๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ์—ฌ๋Ÿฌ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ํ•จ์ˆ˜๋Š” ๋‘˜ ์ค‘ ํ•˜๋‚˜์— ์†ํ•œ๋‹ค

  • ํŠœํ”Œ์˜ ํ•จ์ˆ˜ : ์ธ์ž๋กœ Pair๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”... ์ปค๋ฆฌ๋œ ํ•จ์ˆ˜ : ์ปค๋ฆฌํ•œ ํ•จ์ˆ˜์˜ ๊ฐ ๋‹จ๊ณ„๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๊ฐ๊ฐ ๋ฉ”๋ชจํ™” ํ•ด์•ผํ•œ๋‹ค.
val f3 = { x: Int -> { y: Int -> { z: Int -> x + y - z } } }

// ์ธ์ž๊ฐ€ 3๊ฐœ์ธ ํ•จ์ˆ˜ ๋ฉ”๋ชจํ™”
val f3m = Memoizer.memoize { x: Int ->
                Memoizer.memoize { y: Int ->
                    Memoizer.memoize { z: Int -> x + y - z }
                }
            }

4.5 ๋ฉ”๋ชจํ™”ํ•œ ํ•จ์ˆ˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ธ๊ฐ€

๋ฉ”๋ชจํ™”ํ•œ ํ•จ์ˆ˜๋Š” ์ธ์ž๊ฐ€ ๊ฐ™์œผ๋ฉด ํ•ญ์ƒ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๋†“๋Š”๋‹ค. ๋‹จ์ง€ ๊ฒฐ๊ด๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์ด ๋‹ฌ๋ผ์งˆ ๋ฟ์ด๋‹ค. ์›๋ž˜ ํ•จ์ˆ˜๊ฐ€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ผ๋ฉด ๋ฉ”๋ชจํ™”ํ•œ ํ•จ์ˆ˜๋„ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋‹ค.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment