Lecture 3 Slides - Lecture Slides

1 of 47

Lecture 3

Compound Functions

2 of 47

Rules of computation in Racket

  • If it’s a constant (a number or string)
    • It’s its own value
    • Return it

  • If it’s a variable name (e.g. a word, hyphenated-phrase, or symbol like +)
    • Look up its value in the dictionary
    • Return (output) it

  • If it has parens
    • (i.e. it looks like "(a b c …)”)
    • Find the values of a, b, c, etc. using these same rules
    • The value of a had better be a function
    • Call it with the values of b, c, etc. as inputs
    • Return its output

3 of 47

How do we make a square?

4 of 47

5 of 47

How do we make a row of squares?

6 of 47

7 of 47

How do we make a grid?

8 of 47

9 of 47

Wow, that’s a lot of typing…

(above (beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black"))

(beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black"))

(beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black"))

(beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")))

10 of 47

Wow, that’s a lot of typing…

(above (beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black"))

(beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black"))

(beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black"))

(beside (square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")

(square 50 "outline" "black")))

Why isn't there some way of telling Racket: "whenever I say quack I really mean 'draw a square in outline mode that's black and is size 50'"...

11 of 47

defining new names

12 of 47

Defining new names

(define name value)

  • Tells system that name now refers to value
    • name must be a valid variable name
    • But value can be an arbitrary expression
  • Has to be executed to take effect
  • Naming is the most basic abstraction mechanism
  • Here we're naming a variableafter we define it, anytime we use this name Racket will use whatever value we stored inside it
  • Example: (define netid "abc1234") means that after I run this line of code, whenever Racket reads the name netid and substitute in the value "abc1234"

13 of 47

Simplifying with names

(define unit(square 50 "outline" "black"))

(above (beside unit unit unit unit)

(beside unit unit unit unit)

(beside unit unit unit unit)

(beside unit unit unit unit))

14 of 47

define isn’t a function

Why?

15 of 47

Rules of computation in Racket

  • If it’s a constant (a number or string)
    • It’s its own value
    • Return it

  • If it’s a variable name (e.g. a word, hyphenated-phrase, or symbol like +)
    • Look up its value in the dictionary
    • Return (output) it

  • If it has parens
    • (i.e. it looks like "(a b c …)”)
    • Find the values of a, b, c, etc. using these same rules
    • The value of a had better be a function
    • Call it with the values of b, c, etc. as inputs
    • Return its output

16 of 47

Special forms

  • define is a special case that works differently from function calls
    • For a function call you always replace input expressions with their values
    • But if you’re defining a variable, it doesn’t have a value yet!
    • Plus, what are we getting as an output from it?

  • There are a few special kinds of expressions like this, called special forms
    • We’ll learn a few more, but not very many

17 of 47

This...still isn't that nice to write.

(define unit(square 50 "outline" "black"))

(above (beside unit unit unit unit)

(beside unit unit unit unit)

(beside unit unit unit unit)

(beside unit unit unit unit))

18 of 47

How can we do better?

(Don’t say “loop”; we won’t get to those until next time

19 of 47

Name the row

(define unit (square 50 "outline" "black"))

(define row (beside unit unit unit unit))

(above row row row row)

20 of 47

Your client called

Now they also want one with circles rather than squares

21 of 47

New and improved

(define circ-unit (circle 25 � "outline" � "black"))

(define circ-row� (beside circ-unit circ-unit� circ-unit circ-unit))

(above circ-row circ-row � circ-row circ-row)

22 of 47

Your client called

Now they want a grid of grids of circles rather than squares

23 of 47

What’s wrong with this?

  • Defining unit and row saves us a little bit of work, but …
  • What we really want to do is to name the pattern of making a grids of shapes…
  • We need more abstraction

24 of 47

What we want to be able to say

(grid (circle 25 "outline" "black"))

25 of 47

What kind of a thing is grid?

  • Well, it takes inputs
  • It returns an output
  • It must be a function!!!

  • In fact, we even know what it should do
    • It should make a row from its input
    • Then make a stack of the rows
    • And return it back to us

26 of 47

lambda expressions

27 of 47

Compound functions

(lambda (name1 name2namen) output-expression)

(λ (name1 name2namen) output-expression)

Remember, functions are just another data object!

You can construct new functions from old code using λ expressions

    • name1 name2namen are names to give to the inputs of the function when it’s called
    • output-expression is an expression to compute the output of the function

Note: you type the λ symbol by typing command-\ on Macs or control-\ on Windows

28 of 47

Compound functions

(lambda (name1 name2namen) output-expression)

(λ (name1 name2namen) output-expression)

Remember, functions are just another data object!

When called, the function

  • Substitutes its inputs for name1 name2namen in output-expression
  • Computes the value of the resulting expression
  • Returns it

29 of 47

Compound functions

(lambda (name1 name2namen) output-expression)

(λ (name1 name2namen) output-expression)

If you call (λ (x) (+ x 1)) with an input of 2

  • It take the output expression, (+ x 1)
  • Substitutes 2 in for x to get (+ 2 1)
  • And runs it to get 3
  • 3 is the output of the call

So ((λ (x) (+ x 1)) 2) is a function call

  • The function is (λ (x) (+ x 1))
  • The input is 2
  • It simplifies it to (+ 2 1)
  • And outputs 3

30 of 47

Some New Terminology on Lambdas

  • Functions like rectangle, square, +, -, string-append are what we informally call named functions
  • When we create functions with lambda expressions, there's no requirement to give it a name

> ((lambda (x y) (+ x y))

1 2)

> 3

  • When we don't give a function a name, we call these anonymous functions
    • If we only need to use a function once, there's no reason to give it a name.
  • But if we want to be able to re-use it later, we give it a name using define

> (define my-add (lambda (x y) (+ x y))

> (my-add 1 2)

31 of 47

Non-graphics

Compound Functions and the Substitution Model

32 of 47

Examples of Compound Functions

  • squared is a compound procedure built from *
  • polynomial is a compound procedure built from * (i.e. multiplication), +, and squared
  • Notice they both use the name n for their argument
    • But it’s okay because they’re local names
    • They each have variables named n, but they’re different variables

;; squared: number -> number�;; Compute the square of a number

(define squared� (λ (n) (* n n)))

>(squared 2)

4

> (squared 58.7)

3445.69

;; polynomial: number -> number

;; Compute n2+2n+4

(define polynomial� (λ (n) (+ (squared n)� (* n 2)� 4)))

> (polynomial 32)

1092

33 of 47

the substitution model

34 of 47

The substitution model

  • The SICP book teaches a model of how procedure calls operate called the substitution model
  • Based on algebraic simplification
    • Calls to primitive procedures work by replacing the call with the value of its output
    • Calls to compound functions are computed by
      • Replacing the call with the body of the function
      • Replace the argument names with their values
      • So ((λ (n) (+ n 1)) 5)
        • (+ n 1), remembering n is 5
        • (+ 5 1)

35 of 47

Substitution example

(+ 1 (+ 1 2))��

  • (+ 1 3)

  • 4

36 of 47

Substitution with compound function

(polynomial 32)

substitute value of polynomial

((λ (n) (+ (squared n)� (* n 2)� 4)))� 32)

replace call with body of function

(+ (squared n) (* n 2) 4)

substitute 32 for n

(+ (squared 32) (* 32 2) 4)

(+ (squared 32) (* 32 2) 4)

    • (squared 32)
    • ((λ (n) (* n n)) 32)
    • (* 32 32)
    • 1024
  • (+ 1024 (* 32 2) 4)
  • (+ 1024 64 4)
  • 1092

37 of 47

Back to graphics

38 of 47

Defining grid

(define row� (λ (unit)� (beside unit unit unit unit)))

(define stack� (λ (unit)� (above unit unit unit unit)))

(define grid�(λ (unit)� (stack (row unit))))

39 of 47

Using / Running grid

(define row� (λ (unit)� (beside unit unit unit unit)))

(define stack� (λ (unit)� (above unit unit unit unit)))

(define grid�(λ (unit)� (stack (row unit))))

(grid (circle 25 "solid" "blue"))

40 of 47

Using / Running grid

(define row� (λ (unit)� (beside unit unit unit unit)))

(define stack� (λ (unit)� (above unit unit unit unit)))

(define grid�(λ (unit)� (stack (row unit))))

(grid (circle 25 "solid" "blue"))

41 of 47

notating�type signatures

42 of 47

Type signatures

  • It’s important to keep track of what types of inputs and outputs are required by each function
    • We call this a function's type signature
  • You should always think about the type signature of a function when you use it
    • And whether the types of the inputs match up

43 of 47

Code comments

  • Racket ignores anything after a semicolon (;) in your code
    • Up to the end of the line
  • These are known as comments
    • Nearly all programming languages have them
    • You can use that as a way of writing notes to yourself in the code

44 of 47

Notating type signatures in code

  • It’s good practice when writing functions to notate their type signatures
  • We do that in Racket by using an ->

function name: input types … -> output type

square: number string color -> image

45 of 47

Notating type signatures in code

  • So we’re going to notate that grid, row, and stack expect images as inputs and produce images as outputs:

grid: image -> image

row: image -> image

stack: image -> image

46 of 47

Signature and purpose

; grid: image -> image�; Make a grid of identical pictures

(define grid� (λ (unit)� (stack (row unit))))

  • In this class, when you write a function, you should always start with a comment:
    • Noting its type signature
    • And a short purpose statement: what does the function do?
  • This helps you clarify your thinking about what you’re trying to write
  • If you can’t write these, then you aren’t ready to write the function!

47 of 47

Defining grid

; grid: image -> image�; Make a grid of identical pictures

(define grid�(λ (unit)� (stack (row unit))))

; row: image -> image�; Make a row of identical pictures

(define row� (λ (unit)� (beside unit unit unit unit)))

; stack: image-> image�; Make a stack of identical pictures

(define stack� (λ (unit)� (above unit unit unit unit)))