Programming Languages

Higher Order Structures in Lisp

Jim Posen - ECE/CS 2014

Recursion

  • Function is implemented in terms of itself
  • Calculate answer using recurrence relation
  • You need a base case and a recursive step

The Call Stack

  • Function calls are made on stack
  • Local variables are pushed only the stack
  • Variables are popped off of the stack when function returns

Recursion Example

Here is our recursive definition of the sum function

(define (sum lst)
  (cond
    ((null? lst) 0)
    (else
      (+ (car lst) (sum (cdr lst))))))

Recursion Evaluation

(sum '(1 4 7))
(1 + (sum '(4 7)))
(1 + (4 + (sum '(4 7))))
(1 + (4 + (7 + (sum '()))))
(1 + (4 + (7 + 0)))
(1 + (4 + 7))
(1 + 11)
12

Tail Recursion

  • A recursive call where the return value is the result of the called function
  • This is as opposed to returning a result with an operation applied to it
  • Can be optimized by compiler/interpreter

Tail Recursion Example

(define (sum lst)
  (define (sum-helper lst n)
    (cond
      ((null? lst) n)
      (else
        (sum-helper (cdr lst) (+ (car lst) n)))))
  (sum-helper lst 0))

Tail Recursive Evaluation

(sum-helper '(1 4 7) 0)
(sum-helper '(4 7) 1)
(sum-helper '(7) 5)
(sum-helper '() 12)
12

Higher Order Functions

First-class Functions

  • Functions are first class citizens
  • Functions can be assigned to variables
  • Functions can be passed as function arguments
  • Functions can be returned from functions

Why would we ever want to do this?

Let’s simplify the sum function

(define (sum lst)
  (apply + lst))

Let’s compose two functions

(define (compose f1 f2)
  (define (composed arg1)
    (f1 (f2 arg1)))
  composed)

;; Last element of a list
(define last (compose car reverse))

Anonymous Functions

  • Lambdas are anonymous functions
  • Originated in Lisp (more on this next week)
(let ((double (lambda (x) (+ x x))))
  (double 5))
;; -> 10

Expanded define

(define (double x)
  (+ x x))

(define x 5)

(define double
  (lambda (x)
    (+ x x)))

Abstractions on top of recursion

  • We have done a lot of recursion
  • How do we eliminate the recursive boilerplate?
  • Let’s break down the operations we are doing

Map

  • Map applies a transformation onto each element of a list
  • N element list => N element list

Example Usage

(map (lambda (x) (+ x x)) '(1 4 7 9))
;; '(2 8 14 18)

Let’s implement it!

(define (map proc lst)
  (cond
    ((null? lst) '())
    (else
      (cons (proc (car lst)) (map proc (cdr lst))))))

Filter

  • Filters list to elements for which a predicate is true
  • N element list => (≤ N) element list

Example Usage

(filter odd? '(1 4 7 9 12))
;; '(1 7 9)

Let’s implement it!

(define (filter pred lst)
  (cond
    ((null? lst) '())
    (else
      (let ((rest (filter pred (cdr lst))))
        (if (pred (car lst))
            (cons (car lst) rest)
            rest)))))

Foldl

  • Often called reduce in other languages
  • N element list => scalar
  • Parameters
    • A starting value
    • A list of elements
    • A function with two arguments that returns a scalar
  • Applies procedure to element and value one at a time to produce a scalar

Example Usage

(foldl + 0 '(1 4 7))
;; 12

Let’s implement it!

(define (foldl proc init lst)
  (cond
    ((null? lst) init)
    (else
      (foldl proc
             (proc (car lst) init)
             (cdr lst)))))