For this assignment, you may work either alone or with one partner of your choice.
Remember to follow the pledge you read and signed at the beginning of the semester. For this assignment, you may consult any outside resources, including books, papers, web sites and people, you wish except for materials from previous cs1120, cs150, and cs200 courses. If you use resources other than the class materials, lectures and course staff, explain what you used in a comment at the top of your submission or next to the relevant question.
As always, you are strongly encouraged to take advantage of the scheduled help hours and office hours for this course.
Languages are powerful tools for thinking. One way to solve problems is to think of a language in which a solution to the problem can be easily expressed, and then to implement that language. This is the "great Hop forward" that Grace Hopper made in the 1950s: we can produce programs that implement languages. The input to the program is an expression specification in some language. If the program is an interpreter, the result is the value of that expression.
In this problem set, we provide a working interpreter for the Charme language, which is approximately a subset of Scheme. The interpreter implements the Scheme evaluation rules with state. Your assignment involves understanding the interpreter and making some additions and changes to it. If you are successful, you will produce an interpreter for the language Mesmerize in which the original Fibonacci procedure that includes two recursive calls will run time that is approximately linear in the value of its input (compared to the exponential time it would have with a normal Scheme interpreter), and the simple edit distance procedure from Problem Set 2 runs fast enough for reasonably large inputs (analyzing its actual running time is left as a bonus problem).
It defines a procedure meval that takes two inputs: a list that represents a Charme expression and an environment. It outputs the value of the input expression. The initializeGlobalEnvironment() procedure initializes the global environment in the global variable globalEnvironment. We can use the as the second input to meval. You can try some evaluations using meval directly:
This is a bit awkward since the first input is not in the standard Charme notation, but the parsed structure.
>> meval('23', globalEnvironment)
>>> meval(['+', '1', '2'], globalEnvironment)
The parse procedure takes a string representing one or more Charme expressions and produces as output a list containing each input expression as a structured list. For example:
Since parse takes one or more expressions and produces a list of expressions as its output, we cannot pass the result from parse direction to meval. If we just parse one expression, we just want to pass the first element of the parse output to meval:
>>> parse("(+ 1 2 (* 3 4))")
[['+', '1', '2', ['*', '3', '4']]]
>>> parse("(define square (lambda (x) (* x x)))")
[['define', 'square', ['lambda', ['x'], ['*', 'x', 'x']]]]
>>> meval(parse("(+ 1 2 (* 3 4))"), globalEnvironment)
It is a bit awkward to remember the  and to pass in the globalEnvironment, so we have defined a procedure evalInGlobal(expr) that takes a string representing one Charme expression and evaluates it in the globalEnvironment:
For example:def evalInGlobal(expr): return meval(parse(expr), globalEnvironment)
>>> evalInGlobal("(define square (lambda (x) (* x x)))")
>>> evalInGlobal("(square 4)")
<__main__.Procedure instance at 0x03C0BE18>
We have also provided the evalLoop() procedure that provides an interactions buffer for Charme.
From the perspective of our shiny new interpreter, a Charme program is really just a string that we parse and evaluate.
When you've done it correctly, you should see output like this:
>>> evalInGlobal("(factorial 5)")
>>> evalInGlobal("(<= 5 3)")
>>> evalInGlobal("(<= 3 7)")
You should start by defining a class that represents a cons cell. For example, you might define a Cons class that has a constructor (__init__) that takes two inputs (the first and second parts of the pair), and provides methods for getFirst and getSecond that retrieve the respective parts of the pair.
You must also define a __str__(self): method for your class so that evalLoop and evalToplevelExp will print out Cons sells similarly to how they are displayed in Scheme.
You should get the following interactions in the evalLoop():
Charme> (cons 1 2)
(1 . 2)
Charme> (car (cons 1 2))
Charme> (cdr (cons 1 2))
Or the equivalent ones with evalInGlobal:
>> evalInGlobal("(cons 1 2)")
(1 . 2)
Hint: You could use Python's None value to represent null.
After finishing these questions, you should get the following interactions in the evalLoop():
Charme> (define a (list 1 2 3 4))
Charme> (car a)
Charme> (null? a)
Charme> (cdr (cdr a))
(3 4)Note that this is the way Scheme prints out lists, and you are encouraged to make your Charme interpreter also print out lists this way. It is acceptable, though, to print out lists in a more straightforward way, so they would be displayed as normal cons pairs. Then, this would look like, (3 . (4 . None)), instead.
Charme> (null? (list ))
Charme> (cond ((> 1 2) 2))
Charme> (cond ((> 1 2) 2) ((> 3 2) 3))
Charme> (define fibo (lambda (n) (cond ((= n 1) 1) ((= n 2) 1) (true (+ (fibo (- n 1)) (fibo (- n 2)))))))
Charme> (fibo 10)
To implement memoizing, we need to save the results of all procedure applications in a table. When a procedure application is evaluated, first, we lookup the application in the table to see if it has already been computed. If there is a known value, that is the result of the evaluation and no further computation need be done. If there is not, then the procedure application is evaluated, the result is stored in the table, and the result is returned as the value.
pleased = """ What are you most pleased about regarding this course? """
displeased = """ What are you most displeased about regarding this course? """
hopetolearn = """ What did you most hope to learn in this course that we have not yet covered? """
a. Define a Charme procedure that implements the edit distance procedure from Problem Set 2:
(define (edit-distance s1 s2) (if (or (null? s1) (null? s2)) (+ (length s1) (length s2)) (min (+ (if (eq? (car s1) (car s2)) 0 1) (edit-distance (cdr s1) (cdr s2))) (+ 1 (edit-distance s1 (cdr s2))) ; insert in s1 (+ 1 (edit-distance (cdr s1) s2)))))) ; delete from s1You will need to either extend your Charme interpreter to support the or special form used in edit-distance and the min primitive procedure, or rewrite the edit-distance procedure to only use the language subset already defined.
b. Analyze the running time of your procedure, using the memoized Charme interpreter.
c. Analyze the running time of your procedure, using the standard Scheme interpreter.