Homework 5: Currying and higher order functions
Table of Contents
1. Pair programming structure
This is a "pair" assignment, which means that you and your partner should use the pair programming model. You should be physically together, sharing a computer and both looking at the same screen.
Each partner has an active role to play during pair programming - please review the instructions on Moodle about pair programming, and make sure you understand what you should be doing when you are the navigator and when you are the driver. You should make sure that over the course of an assignment that you each spend roughly the same amount of time "driving." My recommendation is to set a timer and switch drivers every 10-15 minute.
If pair programming in real-time doesn't work for you and your partner, then you will need to amicably split up and work individually. If you choose this option, you must communicate that to me [Anna]. Please do so as soon as that decision is made. If you split, a new partner typically cannot be assigned until the rest of the class switches partners.
2. Get started
Download the folder with the starter code from the link below. Like for previous homeworks, unzip the folder and move it to where you'd like: most likely the folder for the Docker container. Then get started in VS Code.
If when running tests you run into a permissions error, you can fix this by running the following code in the terminal:
chmod a+x test-e test-m
This tells the computer that all users should be permitted to execute (run)
the files test-e and test-m.
3. Main assignment
3.1. Currying
First, here's a super quick overview on currying: a curried function to handle multiplication can be defined as:
(define mult (lambda (a) (lambda (b) (* a b))))
A curried function of two arguments, for example, is one that really just takes one argument, and returns another function to handle the second argument.
Define a function curry3 that takes a three-argument function and returns a
curried version of that function. Thus ((((curry3 f) x) y) z)
returns the result of (f x y z).
3.2. Uncurrying
Define a function uncurry3 that takes a curried three-argument function
and returns a normal Scheme uncurried version of that function. Thus ((uncurry3
(curry3 f)) x y z) returns the result of (f x y z).
3.3. General uncurrying
Heads up: this section is only required for the E tests
Define a function uncurry that takes a curried function of any number of
parameters. It should return a normal Scheme uncurried version of that
function. Thus
((uncurry (curry3 f)) x y z) returns the result of (f x y z), ((uncurry
(curry4 g)) w x y z) would return the result of (g w x y z) if we had written
curry4, and so on. (Thought question for yourself only: why didn't I ask you to also write a general curry function?)
Note that in your uncurry, you'll need to create a
function that handles a variable number of parameters (however many
parameters the curried function took. When the parentheses are omitted
around the parameters in the lambda, Scheme bundles all inputs
into a list. Here's an example:
(define get-all-but-first-input (lambda args (cdr args)))
I can use it as follows:
(get-all-but-first-input 1 2 3) ;; Evaluates to (2 3) (get-all-but-first-input 1 2 3 4 5) ;; Evaluates to (2 3 4 5)
Tips: There are two general approaches here:
- Create a helper function, using a separate
definethat takes in a function and a list as arguments. I think this may be the more straightforward approach. Note that helper functions, like all functions, should have a comment. - Do everything in one function, recursively. If you take this option,
you may find yourself in a situation where you wish you could
somehow call a function on a list of arguments and have it treat
that list as if it were not a list. I.e., you have the list
(1 2 3)and what you'd like to do is(myfn 1 2 3), where each item in the list is a separate argument. The scheme functionapplycan help you in this situation. Dybvig talks about it here.applycan take in a function and a list and act as if that function received the elements of the list as separate arguments. For instance,(apply + '(4 5))is equivalent to(+ 4 5).
3.4. my-filter
We will discuss / have discussed higher-order Scheme functions such as
map, fold-left, and fold-right. Scheme provides another such function called
filter, for which you can find documentation here. Create a function called
my-filter that works just like filter does, but doesn't use filter or any
other higher-order functions.
3.5. set operations
In Scheme sets can be represented as lists. However, unlike lists, the order
of values in a set is not significant. Thus both (1 2 3) and (3 2 1)
represent the same set. Use your my-filter function to implement Scheme
functions (set-union S1 S2) and (set-intersect S1 S2), that handle set union and set
intersection. You may assume that set elements are always atomic, and that there
are no duplicates. You must use my-filter (or the built-in filter, if you
didn't succeed at making my-filter work) in a useful way to do this. You can
also use the built-in function member if you find that useful. For example:
(set-union '(1 2 3) '(3 2 1)) ;; Evaluates to (1 2 3) (set-union '(1 2 3) '(3 4 5)) ;; Evaluates to (1 2 3 4 5) (set-union '(a b c) '(3 2 1)) ;; Evaluates to (a b c 1 2 3) (set-intersect '(1 2 3) '(3 2 1)) ;; Evaluates to (1 2 3) (set-intersect '(1 2 3) '(4 5 6)) ;; Evaluates to () (set-intersect '(1 2 3) '(2 3 4 5 6)) ;; Evaluates to (2 3)
Note that you must use my-filter or filter within your code in order to earn a grade of M or E. Passing the tests alone is not sufficient.
3.6. exists
Use my-filter (or filter) to implement the function exists, which
returns #t if at least one item in a list satisfies a predicate. For example:
(exists (lambda (x) (eq? x 0)) '(-1 0 1)) ;; Evaluates to #t (exists (lambda (x) (eq? x 2)) '(-1 0 1)) ;; Evaluates to #f
Note that your tests may pass if you don't use my-filter or
filter within your code, but the graders will not grant a grade of M or E if you do do not use one of these functions in a non-trivial way.
4. Optional extensions
Work in this section is 100% optional, and not worth any points. Nonetheless, if you're looking for an extra challenge (or a good way to study for exams), these are fun additional exercises to try.
4.1. flatmap
Define a function flatmap that works like map, but if the function returns a list of values, those values are "flattened" for the final list. Here's an example:
(define plus1 (lambda (x) (+ x 1))) (flatmap plus1 '(1 2 3)) ;; Evaluates to (2 3 4) (define doubleup (lambda (x) (list x x))) (flatmap doubleup '(1 2 3)) ;; Evaluates to (1 1 2 2 3 3)
5. Other info
You must use recursion, and not iteration. (To clarify, your code for set-union, set-intersect
and set-exists will probably not use either recursion or iteration – that is fine.) You may not use
side-effects (e.g. set!).
6. How to test and submit your work
6.1. Testing your work
As with previous assignments, there are M tests and E tests. See the section of Scheme Intro 2 labeled "How to test your work" if you need a refresher on how to run these tests.
6.2. Submitting your work
6.2.1. Acknowledging help
Create a file called CollaborationsAndSources.txt, and in it,
indicate in what ways (if any) you collaborated with other people or consulted
outside resources on this assignment.
Did you share strategies with anyone else? Talk about any annoying errors and get advice? These are
fine things to do, and you should note them in the CollaborationsAndSources.txt
file. Give the names of people you talked with and some description of
your interactions. If you used any resources outside of notes taken in class or the homework/lab starter files that
Anna provided, that is also something to note.
If you didn't talk with anyone or use any outside sources, please note
that explicitly in CollaborationsAndSources.txt.
6.2.2. Generating the submission file and uploading to Gradescope
After completing CollaborationsAndSources.txt and finishing the
assignment, run ./zipitup (if you get a permissions error, run chmod u+x ./zipitup first).
This script will create a zip file XXX_submission.zip where XXX is the assignment name or number.
Upload this file to Gradescope.
On Gradescope, make sure to add your partner if you worked with a partner. This page has detailed instructions for how to do that.
On Gradescope, you should see autograder output for the same tests as you ran locally. There is a small chance that the tests will fail on Gradescope, even if they passed on your own computer. Usually this means that you coded something in a different way than we expected when writing the autograders. We will be grading based on the results of the tests on Gradescope, so make sure to pay attention to the results of the tests there. (If you run into this issue, check that you are submitting to the correct assignment on Gradescope. Then try resubmitting and rerunning the autograder. If the error messages are legible, feel free to debug further, but it's also fine to reach out to Anna or the graders at this point.)
6.2.3. Grading
You'll earn at least an M if the following conditions are all met:
- All M tests pass
- A visual inspection of your code shows that you have not hyper-tailored your code to pass the tests. Hyper-tailoring your code would be doing things like checking for the exact input from the test, meaning you haven't written your code in a way that would work for similar inputs.
- You have a
CollaborationsAndSources.txtfile with the information described above.
You'll earn an E if:
- All conditions above for an M are met.
- All the E tests pass.
- Your code is not significantly more complex than needed to accomplish the task.
- You have a comment before each function describing what the function does (its input/output behavior, not a play-by-play of "first checks… then makes a recursive call …").
- Your code follows good style convention (we will get more strict on this as we progress with each language).
If your code does not meet the criteria for an M or an E, then you'll earn a
grade of NA (Not Assessable) or SP (Some Progress) for the assignment. Not Assessable means that your submission does not
have an attempt at one or more functions in the assignment. Some Progress means
that your submission has some code (in the correct language) for all functions in the
assignment, but that the code does not pass all of the M tests, is
hyper-tailored to the tests, and/or is missing a CollaborationsAndSources.txt
file.
You are welcome to revise your submission until the revision deadline (see Gradescope).
If you revise your submission, double-check to make sure that the autograder tests pass and that you
have included CollaborationsAndSources.txt.
Assignment modified from one originally designed by Dave Musicant (with input from Stephen Fruend and John Mitchell) - thanks for sharing!