CS 1321 CS 1321 Introduction to Programming Georgia

  • Slides: 45
Download presentation
CS 1321

CS 1321

CS 1321: Introduction to Programming Georgia Institute of Technology College of Computing Lecture 17

CS 1321: Introduction to Programming Georgia Institute of Technology College of Computing Lecture 17 October 23, 2001 Fall Semester

Today’s Menu Passing functions as parameters Or Now we get really weird • Review:

Today’s Menu Passing functions as parameters Or Now we get really weird • Review: Adding parameters to generalize functions • Passing code as values • Contracts

Last time At the end of the last lecture, we started to explore the

Last time At the end of the last lecture, we started to explore the idea of similarity in functionality and how to program for that similarity. Our first foray into this area of Computer Science dealt with functions that dealt with the exact same data types and very similar problem statements: Given a list-of-symbols, write a function contains -doll? that determines -car? that determines whether or not the symbol ‘doll is symbol ‘car is contained in the list.

Similarities in Functions As we develop more and more functions, one of the things

Similarities in Functions As we develop more and more functions, one of the things we notice is how similar many of our functions are. Often there is that “one little difference” that makes it a different function, however. So what if we could get rid of that one little difference?

It would be relatively trivial for us to write individual functions that solved the

It would be relatively trivial for us to write individual functions that solved the problem statement: Doll (define (contains-doll? in-los) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) ‘doll) true) (else (contains-doll? (rest in-los))))))) Car (define (contains-car? in-los) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) ‘car) true) (else (contains-car? (rest in-los)))))))

But as we see more functions with similar functionality… Doll (define (contains-doll? in-los) (cond

But as we see more functions with similar functionality… Doll (define (contains-doll? in-los) (cond ((empty? in-los) false) Bob (define (contains-Bob? in-los) Truck (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) ‘doll) true) (else (cond ((symbol=? (first in-los) ‘Bob) true) (else (contains-doll? (rest in-los))))))) (else (contains-Bob? (rest in-los))))))) (define (contains-truck? in-los) Song Car (cond ((empty? in-los) false) (define (contains-song? in-los) (cond ((empty? in-los) false) (define (contains-car? in-los)((symbol=? (first in-los) ‘song) true) (else (cond ((symbol=? (first in-los) ‘truck) true) (else (contains-truck? (rest in-los))))))) (cond ((empty? in-los) false) (else (contains-song? (rest in-los))))))) (else (cond ((symbol=? (first in-los) ‘car) true) (else (contains-car? (rest in-los))))))) Orange (define (contains-orange? in-los) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) ‘orange) true) (else (contains-orange? (rest in-los))))))) Ball (define (contains-ball? in-los) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) ‘ball) true) (else (contains-ball? (rest in-los)))))))

Déjà vu! We realize we’re writing the same code over and over again. We

Déjà vu! We realize we’re writing the same code over and over again. We might just copy-paste our solutions and modify what’s appropriate. The leads to typos in editing, and just more work than it is worth. So instead we try to recognize the pattern in our programs and come up with a generic function…

Contains-<value>? (define (contains-<value>? in-los) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) <value>)

Contains-<value>? (define (contains-<value>? in-los) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) <value>) true) (else (contains-<value>? (rest in-los))))))) If it comes down to this every time we write this function, why don’t we just pass a parameter for the value we’re looking for?

Our Solution… (define (contains-symbol? in-los in-symbol) (cond ((empty? in-los) false) (else (cond ((symbol=? (first

Our Solution… (define (contains-symbol? in-los in-symbol) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) in-symbol) (else (contains-symbol? (rest in-los) in-symbol)))))) true)

But wait! The last solution worked very well for searching for some random symbol

But wait! The last solution worked very well for searching for some random symbol in a list of symbols. But what about searching for a number in a list of numbers, or a teaching assistant in a list of teaching assistants? These are all the same problem really as searching for a symbol in a list of symbols. (But of course the way we check for equality varies…) Do we have a pattern we can follow?

Compare and contrast (define (contains-symbol? in-los in-symbol) (cond ((empty? in-los) false) (else (cond ((symbol=?

Compare and contrast (define (contains-symbol? in-los in-symbol) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) in-symbol) true) (else (contains-symbol? (rest in-los) in-symbol)))))) (define (contains-number? in-lon in-number) (cond ((empty? in-lon) false) (else (cond ((= (first in-los) in-number) true) (else (contains-number? (rest in-lon) in-number))))))

Compare and contrast (define (contains-symbol? in-los in-symbol) (cond ((empty? in-los) false) (else (cond ((symbol=?

Compare and contrast (define (contains-symbol? in-los in-symbol) (cond ((empty? in-los) false) (else (cond ((symbol=? (first in-los) Our biggest in-symbol) true) difference seems to (else (contains-symbol? (rest in-los) lie in this area in in-symbol)))))) both functions: the (definetest. (contains-number? in-lon in-number) equality (cond ((empty? in-lon) false) (else (cond ((= (first in-los) in-number) true) (else (contains-number? (rest in-lon) in-number))))))

So what’s our pattern? (define (contains-<value-type>? in-list-of-type value) (cond ((empty? in-list-of-type) false) (else (cond

So what’s our pattern? (define (contains-<value-type>? in-list-of-type value) (cond ((empty? in-list-of-type) false) (else (cond ((<generic equality test> (first in-list-of-type) value) true) (else (contains-<value-type>? (rest in-list-of-type) value))))))

So what’s our pattern? (define (contains-<value-type>? in-list-of-type value) (cond ((empty? in-list-of-type) false) (else (cond

So what’s our pattern? (define (contains-<value-type>? in-list-of-type value) (cond ((empty? in-list-of-type) false) (else (cond ((<generic equality test> (first in-list-of-type) value) true) (else (contains-<value-type>? If we had some way to (rest in-list-of-type) create a generic equality test, we could easily just value)))))) insert it into our pattern and be fine!

We could… (cond ((number? value) <handle numbers>) ((symbol? value) <handle symbols>) ((list-of-numbers? value) <handle

We could… (cond ((number? value) <handle numbers>) ((symbol? value) <handle symbols>) ((list-of-numbers? value) <handle list-of-numbers>) ((list-of-symbols? value) <handle list-of-symbols>) ((posn? value) <handle posn>) ((shape? value) <handle shape>) ((person? value) <handle person>) Create a function called generic-equality that took in ((ta? value) <handle ta>) both the values types ((BT? and value)their <handle BT>) and used our various predicates such as number? or symbol? to ((BST? value) <handle BST>) set up a massive cond statement ((graph? value) <handle graph>)to handle each and every type that we could possibly need… ((list-of-persons? value) <handle list-of-persons>) ((list-of-tas? value) <handle list-of-tas>) ((fresh-fruit? value) <handle fresh-fruit>) ((Georgia-student? value) <hand them a mop>) …

But… This would be cumbersome to the extreme! We’d have to add new equality

But… This would be cumbersome to the extreme! We’d have to add new equality tests every time we encountered a new type. • searching through list testing for equality • searching through list testing for less than • searching through list testing for greater than This is still the same pattern of code!

Recall We talked about a new concept in our exploration of Computer Science: passing

Recall We talked about a new concept in our exploration of Computer Science: passing functionality as a parameter.

And the code… How does this work? Scheme operates on a relatively simple set

And the code… How does this work? Scheme operates on a relatively simple set of rules. One of those rules is as follows:

If we see this: ‘( interpret what follows as a list of items. If

If we see this: ‘( interpret what follows as a list of items. If we see this: ( Interpret what follows as a function call on a list of arguments.

Examples: The list of +, 1, & 2: ‘(+ 1 2) The function call

Examples: The list of +, 1, & 2: ‘(+ 1 2) The function call + on 1 & 2: (+ 1 2) The list of symbol=? , ‘a , ‘b: ‘(symbol=? ‘a ‘b) The function call symbol=? on ‘a, ‘b: (symbol=? ‘a ‘b)

So… If Scheme has determined that it shouldn’t interpret what follows the parenthesis as

So… If Scheme has determined that it shouldn’t interpret what follows the parenthesis as a list, it looks for the function definition that matches the first “name” that follows the parenthesis. Have you ever seen: when trying to do the following?

Scheme is attempting to find a function body associated with the name “a”!

Scheme is attempting to find a function body associated with the name “a”!

Our example… We can pass function “bodies” to our new function and apply them

Our example… We can pass function “bodies” to our new function and apply them to a set of arguments!

Our original problem… Let’s rename our function to contains? (define (contains? in-test in-list value)

Our original problem… Let’s rename our function to contains? (define (contains? in-test in-list value) (cond ((empty? in-list) false) (else (cond ((in-test (first in-list) value) true) (else (contains? in-test (rest in-list) value))))))

Our original problem… Let’s rename our function to contains? (define (contains? in-test in-list value)

Our original problem… Let’s rename our function to contains? (define (contains? in-test in-list value) (cond ((empty? in-list) false) (else (cond ((in-test (first in-list) value) true) Scheme will handle the application of the function to the arguments automatically! (else (contains? in-test (rest in-list) value))))))

Does it work? We pass the testing function, the list to examine, and the

Does it work? We pass the testing function, the list to examine, and the item to look for like so…

Another example Often times you’ll run across a problem that takes in a data

Another example Often times you’ll run across a problem that takes in a data type such as a list and asks you to create a new list containing a selection of the original data that meets a certain criteria. We mentioned two in the last lecture:

Write a function lowerthan that takes in a number and a list of numbers

Write a function lowerthan that takes in a number and a list of numbers and returns a list of numbers in which every value in our list is SMALLER than the inputted number. Write a function higher-than that takes in a number and a list of numbers and returns a list of numbers in which every value in our list is LARGER than the inputted number. We could alter these two problem statements to ask for the selection of items equal to a given number. We could change the problem such that it deals with symbols, structures, entire lists, etc.

The Common Thread When we look at the solutions to the two problem statements

The Common Thread When we look at the solutions to the two problem statements given on the previous slide, we begin to see the pattern….

(define (lower-than in-lon in-num) (cond ((empty? in-lon) empty) (else (cond ((< (first in-lon) in-num)

(define (lower-than in-lon in-num) (cond ((empty? in-lon) empty) (else (cond ((< (first in-lon) in-num) (cons (first in-lon) (lower-than (rest in-lon) in-num))) (else (lower-than (rest in-lon) in-num)))))) (define (higher-than in-lon in-num) (cond ((empty? in-lon) empty) (else (cond ((> (first in-lon) in-num) (cons (first in-lon) (higher-than (rest in-lon) in-num))) (else (higher-than (rest in-lon) in-num))))))

(define (lower-than in-lon in-num) (cond ((empty? in-lon) empty) Our common thread lies in our

(define (lower-than in-lon in-num) (cond ((empty? in-lon) empty) Our common thread lies in our (else (cond ((< (first in-lon) in-num) comparison operator. In the (cons (first in-lon) function it’s a ‘<‘ lower-than comparison between the first (lower-than (rest in-lon) in-num))) item in our list and the (else (lower-than in-lon) inputted (rest number. In in-num)))))) higherthan, it’s the ‘>’ operator. If we were testing for equal, we’d (define (higher-than in-lon in-num) substitute ‘=‘ operator. (cond ((empty? in-lon) empty) To handle symbols, we could (else (cond ((> (first in-lon) in-num) where insert symbol=? appropriate. For structures, (cons (first in-lon) we could create our own (higher-than (rest in-lon) in-num))) equality tests… (else (higher-than (rest in-lon) in-num))))))

So if we took away the “specific” operators in our problems… (define (filter in-list

So if we took away the “specific” operators in our problems… (define (filter in-list in-target) (cond ((empty? in-list) empty) (else (cond ((<GENERIC TEST> (first in-list) in-target) This is the generic (cons (first in-list) pattern of these (filter (rest in-list) in-target))) functions. We insert the specific (else (filter (rest in-list) in-target)))))) test into the function to create lower-than or higher-than

The filter function (define (filter in-test in-list in-target) (cond ((empty? in-list) empty) (else (cond

The filter function (define (filter in-test in-list in-target) (cond ((empty? in-list) empty) (else (cond ((in-test (first in-list) in-target) (cons (first in-list) (filter in-test (rest in-list) in-target))) (else (filter in-test (rest in-list) in-target))))))

Last example: map In the last example, we took in a list data structure

Last example: map In the last example, we took in a list data structure and modified the “shape” of the list by select a portion of it’s contents for the result. Let’s consider instead the case in which we keep the “shape” of the list, but alter the data contained within the list.

Sample problem statements Given a list of numbers representing a temperature in degrees Celsius,

Sample problem statements Given a list of numbers representing a temperature in degrees Celsius, generate a list of numbers containing the equivalent temperature in degrees Fahrenheit. Given a list of TA structures: (make-TA name pay-rate worked) generate a list of symbols containing the names of the TAs. Given a list of TA structures (as defined above), generate a list of numbers containing the amount of pay received by each TA.

Our solutions would look like: Converting to Fahrenheit (define (convert. CF in-lon) (cond ((empty?

Our solutions would look like: Converting to Fahrenheit (define (convert. CF in-lon) (cond ((empty? in-lon) empty) (else (cons ( + (* (first in-lon) 1. 8) 32) (convert. CF (rest in-lon)))))

Our solutions would look like: Extracting TA Names (define (extract-name in-lo. TA) (cond ((empty?

Our solutions would look like: Extracting TA Names (define (extract-name in-lo. TA) (cond ((empty? in-lo. TA) empty) (else (cons (TA-name (first in-lo. TA)) (extract-name (rest in-lo. TA)))))

Our solutions would look like: Calculating TA salary (define (calc-salary in-lo. TA) (cond ((empty?

Our solutions would look like: Calculating TA salary (define (calc-salary in-lo. TA) (cond ((empty? in-lo. TA) empty) (else (cons (* (TA-pay-rate (first in-lo. TA)) (TA-worked (first in-lo. TA))) (calc-salary (rest in-lo. TA)))))

Where is the similarity? In all three cases, we’re applying a function on each

Where is the similarity? In all three cases, we’re applying a function on each and every item of our list. We are then putting the result of this function back on the list. This is also known as mapping a function on every item in our list. The only difference is what the function is. . We can develop the following generic function:

Map (define (map functionality in-list) (cond ((empty? In-list) empty) (else (cons (functionality (first in-list))

Map (define (map functionality in-list) (cond ((empty? In-list) empty) (else (cons (functionality (first in-list)) (map functionality (rest in-list))))))

So how do you denote passing functionality in your contract? We denote function parameters

So how do you denote passing functionality in your contract? We denote function parameters as contracts themselves!

; ; contains? : (X Y boolean) (listof X) Y boolean (define (contains? in-test

; ; contains? : (X Y boolean) (listof X) Y boolean (define (contains? in-test in-list value) …) (X Y boolean) Breaking it down a function that takes in two arguments of unknown type and produces a boolean (listof X) a shorthand the book uses for list-of-X Y a value of unknown type

; ; filter: (X Y boolean) (listof X) Y (listof Z) (define (filter in-test

; ; filter: (X Y boolean) (listof X) Y (listof Z) (define (filter in-test in-list in-target) …) (X Y boolean) a function that takes in two arguments of unknown type and produces a boolean (listof X) a shorthand the book uses for list-of-X Y a value of unknown type (listof Z) a list of results of unknown type

; ; map: (X Y) (listof X) (listof Y) (define (map functionality in-list) …)

; ; map: (X Y) (listof X) (listof Y) (define (map functionality in-list) …) (X Y ) a function that takes in an argument of unknown type and produces a value of unknown type (listof X) a shorthand the book uses for list-of-X (listof Y) a list of results of unknown type