CSci 555: Functional Programming
Recursion Styles, Correctness, and Efficiency
— Scala Version —

H. Conrad Cunningham

6 February 2019

Copyright (C) 2017, 2018, 2019, H. Conrad Cunningham

Acknowledgements: I originally created these slides in Fall 2017 to accompany what is now Chapter 9, Recursion Styles, Correctness, and Efficiency, in the 2018 version of the Haskell-based textbook Exploring Languages with Interpreters and Functional Programming. This Scala version adapts the Haskell-based work for the revised Scala-based notes.

Browser Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of February 2019 is a recent version of Firefox from Mozilla.

Recursion Styles and Efficiency

Lecture Goals

Precondition and Postcondition

Precondition:
What caller (client) must ensure holds when calling function
Constraints on arguments, “global” data structures
Postcondition:
What most hold when function terminates (when precondition holds)
Constraints on retured value, “global” data structure changes

Termination

Every recursive call must move the computation closer to some base case (and it must not be possible to miss the base case).

Time and Space Complexity

Time complexity:
number of execution steps in terms of “problem size” (e.g. parameter values)
Space complexity:
amount of space needed in terms of “problem size” (e.g. parmeter values)

Linear Recursion

Nonlinear Recursion

Linear vs. Nonlinear

Backward Recursion

Forward Recursion (1)

Forward Recursion (2)

    def factIter(n: Int, r: Int): Int = n match {
        case 0          => r
        case m if m > 0 => factIter(m-1,m*r)
    }

Tail Recursion (1)

Tail Recursion (2)

    def fib2(n: Int): Int = {
        def fibIter(n: Int, p: Int, q: Int): Int = n match {
            case 0 => p
            case m => fibIter(m-1,q,p+q)
        }
        if (n >= 0)
            fibIter(n,0,1)
        else
            sys.error(s"Fibonacci undefined for $n")
    }

Logarithmic Recursion (1)

Logarithmic Recursion (2)

    def expt2(b: Double, n: Int): Double = {
        def exptIter(n: Int, p: Double): Double = 
            n match {
                case 0 => p
                case m => exptIter(m-1,b*p)
            }
        if (n >= 0)
            exptIter(n,1)
        else
            sys.error(s"Cannot raise to negative power $n")
    }

Logarithmic Recursion (3)

Logarithmic Recursion (3)

    def exptAux(n: Int): Double = n match {
        case 0                 => 1
        case m if (m % 2 == 0) => // i.e. even
            val exp = exptAux(m/2)
            exp * exp             // backward recursion
        case m                 => // i.e. odd
            b * exptAux(m-1)      // backward recursion
    }

Key Ideas

Source Code