Lab 12 Lambda
Purpose: The purpose of this lab is to provide you with some more practice with higher-order functions, and to demonstrate how they can lead very quickly to very fun code.
Textbook References: Chapter 17: Nameless Functions
Some Notes On λ
The idea behind lambda expressions is very simple, but very subtle. We have the ability to pass functions around as arguments to other functions, but we don’t have any way to write down functions as their own values! In other words, we can write down the following:
In words, this says "define the function double that takes an x and adds it to itself". But we cannot easily write down "define double to be the function that adds its argument to itself". In other words, something like the following:
(define double ....??? something with x and + somehow ???...)
We could write it this way:
(define double (local ((define (the-function x) (+ x x))) the-function))
You can type the λ symbol by pressing Ctrl + backslash.
Whenever you see a lambda-expression, just unwrap it in your head to "the function that takes these arguments and does this thing", without needing the whole local/define boilerplate around it.
Congratulations! You’ve now outgrown ISL! Be sure to change your language for this lab, by clicking in the bottom-left menu and choosing "Intermediate Student + Lambda" instead.
Consider the following function, that squares and adds one to each item in a list of numbers:
(define (sqr+1 lon) (local [ (define (helper num) (+ 1 (sqr num)))] (map helper lon)))
Written much more elegantly with λ:
(define (sqr+1 lon) (map (λ (num) (+ 1 (sqr num))) lon))
Lambdas are useful when they are terse, and used exactly once in your code. Otherwise, give them a name.
Well....almost impossible!
Exercise (Reviewed) 1 ** Design the function biggest-transformation, that takes two Num -> Num functions and outputs a function that given a number num will return the larger result of each function applied to num.
We Are Many. We Are Lambda.
Goals: Practice using functions as data. Practice designing functions that produce functions. Practice using lambda to create anonymous functions. Practice with world programs.
We will design a game in which you can create many balls which move across the screen in various ways. The player can add new balls by clicking on the screen.
Starter Code: Below is the data definition for a Ball.
; A Ball is a (make-ball Nat Mode Color [Nat -> Posn]) (define-struct ball [r mode color placement]) ; INTERPRETATION: ; - r is the ball's radius ; - mode is the ball's mode ; - color is the ball's color ; - placement is a function that, given the current time, ; outputs a new coordinate for the ball to be drawn at ; A Mode is one of: ; - "solid" ; - "outline" (define BALL-1 (make-ball 3 "solid" "red" (λ (x) (make-posn 5 x)))) (define BALL-2 (make-ball 3 "solid" "blue" (λ (x) (make-posn (+ x 2) 3))))
Starter Code: Below is the data definition for a BallWorld, as well as the main function and some graphical constants.
; A BallWorld is a (make-world Nat [List-of Ball]) (define-struct ball-world [t balls]) ; - where t is the amount of time that has passed ; - and balls is the balls of the world (define WORLD-1 (make-ball-world 0 '())) (define WORLD-2 (make-ball-world 10 (list BALL-1 BALL-2))) ; main : [List-of Ball] -> World ; Run the game with this list of initial balls (define (main init-list) (big-bang (make-ball-world 0 init-list) [on-tick (λ (bw) (make-ball-world (add1 (ball-world-t bw)) (ball-world-balls bw)))] [to-draw draw] [on-mouse place-ball])) (define WIDTH 500) (define HEIGHT 500) (define BG (empty-scene WIDTH HEIGHT))
Exercise 2 * Design the function draw-ball that given a Ball, a Posn, and an image, draws the ball at that point on that image.
Exercise 3 ** Design the function make-drawer that given a time will create a function that takes a Ball and an image and will draw it. Hint: Use draw-ball.
Exercise 4 ** Design the function draw that draws the ball world. Look at the helpers you’ve just written: especially the signature for make-drawer. What does it remind you of? How can you use that to help you?
; A BallGenerator is a [Nat Nat Nat -> [Nat -> Posn]] ; Given the time, x-coordinate, and y-coordinate of when and where a ; ball is created, create a function that, given the current time of ; the world, will output a Posn ; Example: ; move-horizontally : BallGenerator (define (move-horizontally t0 x0 y0) (λ (t) (make-posn (modulo (+ x0 (- t t0)) WIDTH) y0))) (check-expect ((move-horizontally 3 5 8) 10) ; 7 seconds have passed (make-posn 12 8))
Exercise 5 * Design the BallGenerator move-vertically.
Exercise 6 * Create a constant GENERATORS, which is a [List-of BallGenerator] that will keep track of all the BallGenerators you’ve made thusfar.
Exercise 7 ** Design the function place-ball, that given a BallWorld, x- and y- coordinate, and MouseEvent will output a BallWorld with a Ball added to it if the person clicked (this is represented by the MouseEvent "button-down"). Feel free to use any radius, color, and mode you like.
As far as the ball’s placement: look at the data definition of a Ball, the signature of this function, and the data definition of a BallGenerator. Feel free to use any BallGenerator you like, as long as it’s used correctly!
Exercise 8 * Now comes the fun stuff. Design a function select-random, that given any non-empty list will select a random element from the list.
Exercise 9 * Update place-ball to select a random BallGenerator from the GENERATORS you previously defined.
Exercise 10 * Update place-ball to make a radius of random size. Make sure you don’t make radiuses of size 0, though, those aren’t very fun!
Exercise 11 * Update place-ball to use a random make-color.
Exercise 12 * Update place-ball by changing "button-down" to "drag".
Before you go...
If you had trouble finishing any of the exercises in the lab or homework, or just feel like you’re struggling with any of the class material, please feel free to come to office hours and talk to a TA or tutor for additional assistance.
Functions on Functions
Goals: Practice using functions as data. Practice designing functions that produce functions. Practice using lambda to create anonymous functions.
Exercise 13 ** Design the function two that takes a function f : [X -> X] as input and returns another function that applies f twice in a row. That is, two returns a function which first applies f to its input, then applies f again to the output of the first application (all within one function call).
Exercise 14 * Design the function three, similarly, that applies a function f three times in a row.
Exercise 15 ** Design the functions one and zero. Writing one is easy, but what about zero? What does it mean to apply a function to its argument zero times?
The functions zero, one, two, and three look curiously similar to numbers: all they do is repeat their input some number of times. Let’s call functions like these Repeaters:
; A Repeater is a function [[X -> X] -> [X -> X]] ; That, given a one-argument function f, outputs a ; function that will repeatedly apply f some specific number of times
Exercise 16 ** Design a function rep->nat which consumes a Repeater as input and produces the number of times it repeats its argument. Here are some tests:
(check-expect (rep->nat zero) 0) (check-expect (rep->nat one) 1) (check-expect (rep->nat two) 2) (check-expect (rep->nat three) 3) (check-expect (rep->nat (λ (f) (λ (x) ((three f) ((two f) x))))) 5) Hint: If you have a Repeater, all you can do is give it some inputs and see what it gives you back. So your task here is simply to devise some inputs that will force it to tell you which number it represents.Note: You should use this function only for debugging or test cases; do not use it to implement any of the functions below.
Exercise 17 ** Design the function rep-add1 that increments a Repeater by 1. Remember, do not use rep->nat; you don’t need it!
Exercise 18 ** Design the function nat->rep that converts a Nat n to the Repeater that repeats its input n times.
Exercise 19 *** Design the function rep+, that takes two Repeaters and produces a new Repeater that represents their sum. Remember, do not use rep->nat; you don’t need it!
Exercise 20 *** Design the function rep*, that takes two Repeaters and produces a new Repeater that represents their product. Remember, do not use rep->nat or rep+; you don’t need them!
Exercise 21 *** Design the function rep^, that takes two Repeaters and produces a new Repeater that represents the first raised to the power of the second. Remember, do not use rep->nat, rep+, or rep*; you don’t need them!