CS61A Homework 7
Homework 7 Solutions hw07.zip
Solution Files
You can find the solutions in hw07.scm.
Scheme is a famous functional programming language from the 1970s. It is a dialect of Lisp (which stands for LISt Processing). The first observation most people make is the unique syntax, which uses a prefix notation and (often many) nested parentheses (see http://xkcd.com/297/). Scheme features first-class functions and optimized tail-recursion, which were relatively new features at the time.
You may find it useful to try code.cs61a.org/scheme when working through problems, as it can draw environment and box-and-pointer diagrams and it lets you walk your code step-by-step (similar to Python Tutor). Don’t forget to submit your code through Ok though!
Scheme Editor
As you’re writing your code, you can debug using the Scheme Editor. In your scheme
folder you will find a new editor. To run this editor, run python3 editor
. This should pop up a window in your browser; if it does not, please navigate to localhost:31415 and you should see it.
Make sure to run python3 ok
in a separate tab or window so that the editor keeps running.
If you find that your code works in the online editor but not in your own interpreter, it’s possible you have a bug in code from an earlier part that you’ll have to track down. Every once in a while there’s a bug that our tests don’t catch, and if you find one you should let us know!
Required Questions
Keyword Lists
In the following problems, you will explore creating two separate implementations for the same abstraction.
A keyword list is the Scheme analogue of a dict
in Python, with a few key differences:
- It allows for repeating keys
- It functions as a list as well, which allows for ordering.
The kwlist
abstraction keeps a mapping of keys
and values
. To create a kwlist
, call the constructor (make-kwlist keys values)
where keys
is a Scheme list of symbols and values
is a Scheme list of any type. This returns some abstracted item lst
that we can call the following methods to either retrieve or add items:
1 | scm> (define lst (make-kwlist '(x y z) '(7 8 9)) ; create the keyword list |
Q1: Keyword List: Construct
First, implement abstractions for kwlist
in two ways, with the following example: (kwlist '(x y z) '(7 8 9))
kwlist1
, which stores a keyword list in the following manner:((key1 key2 key3 ...) (value1 value2 value3 ...)
. With the example above, this should look like((x y z) (7 8 9))
.kwlist2
, which stores a keyword list in the following manner:((key1 value1) (key2 value2) ...)
. With the example above, this should look like((x 7) (y 8) (z 9))
.
Specifically, implement constructors and selectors for kwlist1
and kwlist2
.
- The constructors,
make-kwlist1
andmake-kwlist2
, should take in Scheme lists for bothkeys
andvalues
, and construct the abstraction as above. - The selectors,
get-keys-kwlist1
,get-keys-kwlist2
,get-values-kwlist1
, andget-values-kwlist1
, should take in akwlist1
orkwlist2
and return their keys and values respectively. Note that because you are currently creating the implementation, you are “under the abstraction barrier;” feel free to refer to specific details of the structure ofkwlist1
andkwlist2
.
Hint: The
map
function may prove to be useful, but is not required. You may also use thecadr
function, which is defined for you in the file.
1 | scm> (define ex-lst1 (make-kwlist1 '(a b c) '(1 2 3))) |
Use Ok to test your code:
1 | python3 ok -q kwlist_construct✂️ |
Important: For the following questions, your implementations should be invariant with respect to the abstraction used; that is, it should work regardless of whether
kwlist1
orkwlist2
is used. Specifically, in the tests, we will define the abstractionkwlist
as eitherkwlist1
orkwlist2
:
1
2
3
4
5
6
7
8 scm> (define make-kwlist make-kwlist1)
scm> (define get-keys-kwlist get-keys-kwlist1)
scm> (define get-values-kwlist get-values-kwlist1)
; tests here...
scm> (define make-kwlist make-kwlist2)
scm> (define get-keys-kwlist get-keys-kwlist2)
scm> (define get-values-kwlist get-values-kwlist2)
; tests here...You should refer to the above
kwlist
procedures, notkwlist1
orkwlist2
’s procedures in your implementation.
Q2: Keyword List: Add
Now, implement add-to-kwlist
, which implements support for adding a new (key
, value
) pair to any implementation of a kwlist
. Specifically, add-to-kwlist
takes in a kwlist
, a key
, and a value
as input, and returns a new kwlist
with updated keys and values. Note that kwlist
s are ordered; that is, a pair p1
that was added to a kwlist
before a different pair p2
should appear earlier in the kwlist
.
Hint: The
append
method may be useful here. To make your implementation work with both abstractions, be sure to use methods ending inkwlist
, notkwlist1
orkwlist2
.
1 | scm> (define ex-lst (make-kwlist '(a b c) '(1 2 3))) |
Use Ok to test your code:
1 | python3 ok -q kwlist_add✂️ |
Q3: (Optional) Keyword List: Get
Now, implement get-first-from-kwlist
, which implements support for getting the first value bound to a key
in kwlist
. If key
is not present in the list, the function should return nil
to indicate that there were no valid keys found.
Hint: Consider using
let
to temporarily bind names to values. To make your implementation work with both abstractions, be sure to use methods ending inkwlist
, notkwlist1
orkwlist2
.
1 | scm> (define ex-lst (make-kwlist '(a b c) '(1 2 3))) |
Use Ok to test your code:
1 | python3 ok -q kwlist_get✂️ |
Programs as Data
Note that the following question is separate from the previous questions.
Q4: Prune
Implement prune-expr
, a procedure that takes in an expression, which is represented as a list, and returns the same expression with every other argument included. The operator should not be modified.
Hint: You may find it helpful to write a helper function that prunes a list.
The behavior of prune-expr
is specified by the following doctests:
1 | scm> (prune-expr '(+ 10 20)) |
Use Ok to test your code:
1 | python3 ok -q prune-expr✂️ |
Chef Curry
Recall that curry
ing transforms a multiple argument function into a series of higher-order, one argument functions. In the next set of questions, you will be creating functions that can automatically curry a function of any length using the notion that programs are data!
Q5: Cooking Curry
Implement the function curry-cook
, which takes in a Scheme list formals
and a quoted expression body
. curry-cook
should generate a program as a list which is a curried version of a lambda function. The outputted program should be a curried version of a lambda function with formal arguments equal to formals
, and a function body equal to body
. You may assume that all functions passed in will have more than 0 formals
; otherwise, it would not be curry-able!
For example, if you wanted to curry the function (lambda (x y) (+ x y))
, you would set formals
equal to '(x y)
, the body
equal to '(+ x y)
, and make a call to curry-cook
: (curry-cook '(x y) '(+ x y))
.
1 | scm> (curry-cook '(a) 'a) |
Use Ok to test your code:
1 | python3 ok -q curry_cook✂️ |
Q6: Consuming Curry
Now that you have a function that creates lambda programs as lists, create a function which is able to evaluate lambda functions using a series of arguments. Specifically, implement the function curry-consume
, which takes in a curried lambda function curries
(not a list), and apply
s the function to a list of arguments args
. Similarly to the previous question, you may make several assumptions:
- If
curries
is ann
-curried function, then there will be at mostn
arguments inargs
. - If there are 0 arguments, then you may assume that
curries
has been fullyapply
’d with relevant arguments; in this case,curries
now contains a value representing the output of the lambda function. Return it.
Note that there can be fewer args
than formals
for the corresponding lambda function curries
! In the case that there are fewer arguments, curry-consume
should return a curried lambda function, which is the result of partially apply
ing curries
up to the number of args
provdied.
1 | scm> (define three-curry (curry-cook '(x y z) '(+ x (* y z)))) |
Use Ok to test your code:
1 | python3 ok -q curry_consume✂️ |
Optional Questions
Homework assignments will also contain prior exam-level questions for you to take a look at. These questions have no submission component; feel free to attempt them if you’d like a challenge!