In this lab, you will explore higher-order functions in Lisp by
implementing filter and using it to build several useful
abstractions including set operations and quantifiers.
You may work with a partner on this lab if you like, but it is not required. If you'd like to be matched with a partner, let the instructor know.
You will receive an email with an invitation link to the lab2 assignment on GitHub Classroom. Here's what to expect:
Click the invite link in the email. This takes you to GitHub Classroom. If prompted, authorize GitHub Classroom to access your GitHub account.
Accept the assignment by clicking the green "Accept this assignment" button.
Wait for your repository to be created. GitHub Classroom will copy the starter code into a new private repository for you. This usually takes a few seconds.
Look for the link to your new repository on the confirmation
page. You may need to refresh the page if it says the repository
is still being created or no longer exists.
You can also check your email for a
notification from GitHub with a link to your new repo.
Some of you had authentication issues with GitHub last week. The instructions below will help you set up you account on our Unix machines so that you can clone, push, and pull using an ssh key for authentication. If you have already set up a key, you can just skip ahead to the lab below. These steps save the ssh key in your home directly, so you should only need to do this once.
Generate an SSH key pair. First, check if you already have a key by running
cat ~/.ssh/id_rsa.pub
If that prints a key, skip to step 2. Otherwise, run:
ssh-keygen -t rsa
When prompted for a file location, press Enter to accept the default. You can also press Enter to skip setting a passphrase (or set one if you prefer).
Copy your public key. Display it with:
cat ~/.ssh/id_rsa.pub
Select and copy the entire output.
Add the key to GitHub. Go to github.com/settings/keys, click New SSH key, give it a title (e.g., "CS lab machine"), paste the key into the Key field, and click Add SSH key.
Test the connection:
ssh -T git@github.com
You should see a message like
Hi username! You've successfully authenticated....
Clone using the SSH URL. When cloning your repository,
use the SSH URL (starts with git@github.com:) rather than
the HTTPS URL:
git clone git@github.com:williams-cs/cs334-lab2-YOURNAME.git
We've already seen how using mapcar provides a generic way to
easily manipulate collections of data. There are others that are
equally useful. We examine one of them in this lab.
a. Write a function filter that takes a predicate function p
and a list l. This function returns a list of those elements
in l that satisfy the criteria specified by p. For example,
the following two examples filter all negative numbers out of a
list and filter all odd numbers out of a list:
* (filter #'(lambda (x) (>= x 0)) '(-1 1 2 -3 4 -5))
(1 2 4)
* (defun even (x) (eq (mod x 2) 0))
* (filter #'even '(6 4 3 5 2))
(6 4 2)
You will need to use the built-in operation funcall to call
the function passed to filter as a parameter. That is, the
function
(defun example (f)
(funcall f a1 ... an)
)
applies f to arguments a1 -- an. You may not use the
built-in functions remove-if and remove-if-not in your
solution.
b. Suppose that we are using lists to represent sets (in which
there are no repeated elements). Use your filter function to
define functions set-union and set-interset that take the
union and intersection of two sets, respectively:
* (set-union '(1 2 3) '(2 3 4))
(1 2 3 4)
* (set-intersect '(1 2 3) '(2 3 4))
(2 3)
You may find the built-in function (member x l) described in
the 334 Lisp FAQ handy.
c. Now, use filter to implement the function exists. Given a
predicate function p and a list l, this function returns
true if there is at least one a in l such that (p a)
returns true:
* (exists #'(lambda (x) (eq x 0)) '(-1 0 1))
t
* (exists #'(lambda (x) (eq x 2)) '(-1 0 1))
nil
You may assume that p will terminate without crashing for all
a.
The function all returns true if (p a) is true for all a
in l:
* (all #'(lambda (x) (> x -2)) '(-1 0 1))
t
* (all #'(lambda (x) (> x 0)) '(-1 0 1))
nil
The function count-elems returns the number of elements in l for
which (p a) is true:
* (count-elems #'(lambda (x) (> x 0)) '(-1 0 1 2 3))
3
* (count-elems #'even '(1 2 3 4 5 6))
3
You should not need to recursively traverse directly for any of these.
Make sure your code uses count-elems.
If you were in the 11am lab, you may have starter code that uses count instead of count-elems.
This will cause an error since clisp doesn't let you redefine that symbol.
d. In a comment in your code, explain in 1-2 sentences why
using filter to implement exists and all may not be the
most efficient approach.
Implement apply-n-times, which applies a function f to a
value x repeatedly n times:
* (apply-n-times #'cdr 2 '(a b c d e))
(c d e)
* (apply-n-times #'(lambda (x) (* x 2)) 5 1)
32
* (apply-n-times #'(lambda (x) (cons 'a x)) 3 nil)
(a a a)
Then use apply-n-times to implement nth-elem, which returns the
nth element of a list (0-indexed):
* (nth-elem 0 '(a b c d))
a
* (nth-elem 2 '(a b c d))
c
Make sure your code uses nth-elem.
Be sure your code uses nth-elem instead of nth, for the same
reason as above.
Consider this Python code that counts how many words in a list have more than 4 letters:
def count_long_words(words):
count = 0
for word in words:
if len(word) > 4:
count += 1
return count
a. Rewrite this in Python using a list comprehension instead of a loop. Your solution should be one line.
b. Which version do you find more readable? (Note: This is asking you to compare the two Python versions: the loop version and the comprehension version. You can ignore the 3-python-crossover.lisp file if you see it in your repo -- that was inadvertently included and I removed it, possibly after you cloned the starter.) Write 1-2 sentences
comparing the approaches. Write your answer in a comment in your python file.
Submit your code to the GradeScope assignment named, for example, "Lab 1". You can submit in one of two ways:
Please do not change the names of the starter files. Also:
Autograding: Gradescope will run an autograder on your code that performs some simple tests. Be sure to look at the autograder output to verify your code works as expected. We will run more extensive tests on your code after the deadline.