Function Design

advertisement

Function Design in LISP

Program Files

 LISP programs are plain text

– DOS extensions vary; use .lsp for this course

(load “filename.lsp”)

 Can use Unix-style paths

– (load “c:/Work/Comp2043/A4/myFile.lsp”)

– (load “A4/myFile.lsp”)

– (load “A4\\myFile.lsp”) OK in Windows

Comments

 For this course:

– identify file as we did with Prolog programs

– function comments similar to predicates

– also say what gets returned

;; (fib N)

;; -- returns the Nth Fibonacci number, N > 0

;; -- no error checking (infinite loop if N =< 0)

Comments

 Many similar functions

– conversion functions, for example

 One comment for all

;; (print-no-X-warning N)

;; -- where X in {student, course, section}

;; -- print a warning message that there is no

;; object X corresponding to N

;; -- N is a student number, course number, etc.

Functional Cohesion

 You should be able to describe what your function does in a simple sentence

– how it’s doing it may require more explanation

If you need the word “and”, you’re probably doing too much!

– see if you can split it into two functions…

– …or move some functionality into another f n

Non-Cohesive Function

;; (squeeze-input)

;; -- rewrite output getting rid of excess spaces

;; (punc L)

;; -- checks whether L is punctuation

;; and if it isn’t it prints a space

;; and continues “squeezing” input

Cohesive Function

;; (squeeze-input)

;; -- rewrite output getting rid of excess spaces

;; (punc L)

;; -- says whether L is a punctuation character

 Let (squeeze-input) worry about printing spaces and continuing squeezing input

Writing Functions in LISP

First (as usual): understand what’s required

– what are you given?

– what is the value to return?

 Second: plan how to get result

– break down into its conditions (if any)

– identify easy cases

– break harder cases down into parts

Functions and Lists

 List argument generally needs to be broken into parts

– (first L) – operate on this directly

– (rest L) – recur on this

 Mapped parts need to be re-combined

– maybe some math function – atomic result

– usually using CONS – list result

Implementing a Plan

 Plan is in steps:

– calculate this

– use it to calculate that

 All must be combined into one function call

 Early calculations become arguments for later ones

– will evaluate from the inside out

Plus-1 List: Imperative

 Add one to each element of a list plus1List(L) if (L == nil) return nil; variables not necessary else var newFirst

 first(L) + 1; var newRest

 plus1List(rest(L)); return cons(newFirst, newRest);

Plus-1 List: Imperative

 Add one to each element of a list plus1List(L) if (L == nil) else return nil; replace conditional command with conditional expression return cons(first(L) + 1, plus1List(rest(L)));

Plus-1 List: Imperative

 Add one to each element of a list plus1List(L) return (L == nil) ?

nil

: now functional: rewrite to LISP cons(first(L) + 1, plus1List(rest(L)));

Plus-1 List: LISP

 Add one to each element of a list

(defun plus1List (L)

(if (null L) nil make more “idiomatic”

(cons (+ (first L) 1)

(plus1List (rest L)))))

Plus-1 List: LISP

 Add one to each element of a list

(defun plus1List L

(unless (null L)

(cons (+ (first L) 1)

(plus1List (rest L)))))

(unless (null Arg)

(cons (…(first Arg)…)

(…(rest Arg)…))) programming idiom translating a list

Functional Abstraction

 Complicated (or repeated) steps should get their own function definitions

– apply same process again

– understand what you want out of the function

(don’t let the code control you)

 Laziness is a virtue

– leave it for later

– but make sure you understand it, first

Example

 Standard deviation (population)

– square root of the average of the deviations

– given a list of numbers, returns a number

 Functional abstraction

– square root is built in, average we can build

– deviations??? Time to be lazy!

(defun stdevp (L) (sqrt (average (deviations L))))

Sub-Problems

 Average

– given a list of numbers, return a number

– add up the numbers, divide by the # of numbers

– left as an exercise

 Deviations

– deviation = difference from average, squared

– given a list of #s, return another list of #s

Deviations

 Need the average of the list

– use the average function:

(average L)

 Need to find the deviation for every element in the list – its difference from the average

– time to be lazy again – another function

– given a list & its average, return list of deviations

– (deviation-list L (average L))

Deviation List

 Need to process a list & return a list

– break down list (using first & rest)

– calculate deviation (separate function on first, recursion on rest)

– reconstruct list (using cons)

 Unless the list is empty, of course

(unless (null L) (cons (…(first L)…) (…(rest L)…)))

Calculation of deviation of first

Calculation of deviations of rest

Calculating a Single Deviation

 Given a number and the mean, return a number (its deviation)

– their difference, squared

– use expt for squaring (expt x 2) = x 2

(defun calculate-deviation (Value Mean)

(expt (– Value Mean) 2))

List of Deviations

(defun deviation-list (L M)

(unless (null L)

(cons

(calculate-deviation (first L) M)

(deviation-list (rest L) M)

) ) )

(defun deviations (L)

(deviation-list L (average L)))

Function Call

 Calculate standard deviation (population) of the list (5 10 15)

> (stdevp ‘(5 10 15))

(sqrt (average (deviations ‘(5 10 15))))

(average (deviations ‘(5 10 15)))

(deviations ‘(5 10 15))

(deviation-list ‘(5 10 15) (average ‘(5 10 15)))

= (deviation-list ‘(5 10 15) 10)

Function Call (cont.)

(deviation-list ‘(5 10 15) 10)

(unless (null ‘(5 10 15)) (…))

= (unless NIL (cons (…) (…)))

(cons (calculate-deviation …) (…))

(calculate-deviation (first ‘(5 10 15)) 10)

= (calculate-deviation 5 10)

(expt (– 10 5) 2) = 25

= (cons 25 (deviation-list (rest ‘(5 10 15)) 10))

Function Call (cont.)

(deviation-list ‘(10 15) 10)

(unless (null ‘(10 15)) (…))

= (unless NIL (cons (…) (…)))

(cons (calculate-deviation …) (…))

(calculate-deviation (first ‘(10 15)) 10)

= (calculate-deviation 10 10)

(expt (– 10 10) 2) = 0

= (cons 0 (deviation-list (rest ‘(10 15)) 10))

Function Call (cont.)

(deviation-list ‘(15) 10)

(unless (null ‘(15)) (…))

= (unless NIL (cons (…) (…)))

(cons (calculate-deviation …) (…))

(calculate-deviation (first ‘(15)) 10)

= (calculate-deviation 15 10)

(expt (– 10 15) 2) = 25

= (cons 25 (deviation-list (rest ‘(15)) 10))

Function Call (cont.)

(deviation-list ‘() 10)

(unless (null ‘()) (…))

= (unless T (cons (…) (…))) = NIL

= (cons 25 NIL) = (25)

= (cons 0 ‘(25)) = (0 25)

= (cons 25 ‘(0 25)) = (25 0 25)

= (average ‘(25 0 25)) = 16.6667

= (sqrt 16.6667) = 4.0825

Tweaking the Code

 Deviations just calls deviation-list with own argument plus another

 Could be combined into one function

– make the mean an optional argument

– default value – average of the given list

(defun deviations (L &optional (M (average L))) …)

 Will call average if & only if no mean given

Combined Deviations Code

(defun deviations (L &optional (M (average L)))

(unless (null L)

(cons

(calculate-deviation (first L) M)

(deviations (rest L) M)

) ) )

 Note: important to pass M in recursive call

– else calculates average of (10 15) = wrong

Optional Parameters

 Use &optional in parameter list

– everything before &optional is required

– if not given use NIL – or default value (next)

> (defun opt-args (a &optional b c) (list a b c))

OPT-ARGS

> (list (opt-args 1) (opt-args 2 3) (opt-args 4 5 6))

((1 NIL NIL) (2 3 NIL) (4 5 6))

Default Values

 For optional arguments

– list with parameter name & default value

– default calculated at call time

> (defun def-args (a &optional (b 5)) (list a b))

DEF-ARGS

> (list (def-args 1) (def-args 2 3))

((1 5) (2 3))

Correlation

 Write a function to calculate the correlation between two lists

– the two lists must be the same length

 Understanding

– given two lists (same length)

– returns a number

– formula to follow

Calculating a Correlation

 Complicated formula requires:

– length of lists (N)

– sums of lists (Sx and Sy)

– dot product of the two lists (Sxy)

– sums of squares of lists (Sxx and Syy)

 Result is

(N*Sxy – Sx*Sy) sqrt((N*Sxx) – (Sx) 2 )*(N*Syy – (Sy) 2 ))

What Do We Need?

 Functions that calculate:

– length of a list

– sum of a list

– dot product of two lists

– sum of squares of list

Assume We Have Them

 Write the result calculation function

(N*Sxy – Sx*Sy) sqrt((N*Sxx) – (Sx) 2 )*(N*Syy – (Sy) 2 ))

 Write the main function

– N = length of lists (must be same for both)

– Sx = sum of list 1 st list, Sy = sum of second

– Sxy = dot product of lists

– Sxx = sums of squares of 1 st list, Syy = ditto 2 nd

Now for the Helpers

 Already have the length function built in

 Write the sum of a list function

 Write the dot product function

– (1 2 3) * (4 5 6) = (1*4)+(2*5)+(3*6) = 32

 Write the sum of squares function

– note: can use dot product function

Final Result

(defun correlation (Xs Ys) …)

(defun correlation-calc (N Sx Sy Sxy Sxx Sxy) …)

(defun sum-of-list (L) …)

(defun dot-product (Xs Ys) …)

(defun sum-of-squares (L) …)

>

(correlation ‘(5 10 15) ‘(3 6 12))

0.989

Exercise

 Write a function that calculates the correlation of a list of pairs

– (correlate-pairs ‘((5 3) (10 6) (15 12))) => 0.989

– list of (X Y) pairs

 Use the correlation function from last time

– (correlation ‘(5 10 15) ‘(3 6 12)) => 0.989

– only difference is the form of the data

– ( write it in two lines! (hint: be lazy) )

Exercise

 Use the list translation programming idiom we discussed earlier to write the rest of the support functions for our new correlations function

– translate list of XY pairs into a list of Xs

– translate list of XY pairs into a list of Ys

 Control over lists

– Chapter 6

Next Time

Download