f
may contain direct calls to f
itself.f
may be indirectly recursive if it does not call f
itself, but one of the functions g
that f
calls directly calls f
(either directly or indirectly).f
and g
are such that f
directly calls g
and g
directly calls f
, we say that f
and g
are mutually recursive.A canonical example of recursive functions is the factorial
function:
fun factorial(n: Int): Int = if (n == 0) 1 else n * factorial(n-1)
There’s something to be learned even from this simple example.
The image on the left shows the starting position and and the one on the right shows the ending position.
Here is one possible solution to the ToH problem.
/*
* Prints instructions to optimally move n discs from peg `src`
* to peg `des` using peg `aux` as the auxillary peg.
*/
fun hanoi(n: Int, src: Int, aux: Int, des: Int) {
if (n == 1)
println("move top disc from peg $src to peg $des")
else {
hanoi(n-1, src, des, aux)
println("move top disc from peg $src to peg $des")
hanoi(n-1, aux, src, des)
}
}
fun main(args: Array<String>) {
val n = args[0].toInt()
if (n > 0) hanoi(n, 1, 2, 3)
}
Exercise: Come up with a more economical solution (shorter code and/or fewer parameters).
A good example is the H-tree, a self-similar fractal tree structure with applications in VLSI design and microwave engineering. The picture below shows the H-trees of level 1 through 8. Can you describe how the level-\(n\) H-tree is constructed from the level-\((n-1)\) H-tree?
This Kotlin code draws the even-level H-Trees.
import processing.core.PApplet
fun main(args: Array<String>) {
PApplet.main("EhtreeSketch");
}
class EhtreeSketch : PApplet() {
var level = 0
override fun settings() {
//size(640, 640)
fullScreen()
noLoop()
}
override fun draw() {
background(250)
ehtree(width/2F, height/2F, width.toFloat(), height.toFloat(), ++level)
level %= 7
}
override fun keyPressed() {
redraw()
}
/*
* Draw a level-2n Htree in the rectangle of width `width`
* and height `height` centered at point (x, y).
*/
fun ehtree(x: Float, y: Float, w: Float, h: Float, n: Int) {
if (n < 1) return
val x0 = x - w / 4F
val x1 = x + w / 4F
val y0 = y - h / 4F
val y1 = y + h / 4F
line(x0, y, x1, y)
line(x0, y0, x0, y1)
line(x1, y0, x1, y1)
ehtree(x0, y0, w / 2F, h / 2F, n - 1)
ehtree(x0, y1, w / 2F, h / 2F, n - 1)
ehtree(x1, y0, w / 2F, h / 2F, n - 1)
ehtree(x1, y1, w / 2F, h / 2F, n - 1)
}
}
Htree.kt
using the processing library such that calling java HtreeKt n
will draw a level-n
Htree.For example, this version of factorial
(to be called with factorial(n, 1)
when computing \(n!\)) is tail-recursive
fun factorial(n: Int, acc: Int): Int =
if (n == 0) acc else factorial(n-1, acc*n)
factorial(n-1, acc*n)
returns, the caller function immediately returns with that result.The original version here
fun factorial(n: Int): Int = if (n == 0) 1 else n * factorial(n-1)
is not tail-recursive because after the called function factorial(n-1)
returns, the caller function has to multiply that returned result by n
before returning with the new result.
However, you have to tell the compiler to activate this optimization feature. All you have to do is to put the keyword tailrec in front of the keyword fun at program definition, like this:
tailrec fun factorial(n: Int, acc: Int): Int =
if (n == 0) acc else factorial(n-1, acc*n)