Logo
  • Home
  • Classes
  • Conveying Computing
  • Exams
  • Fractal Gallery
  • Guides
  • Problem Sets
  • Syllabus

Problem Set 7: Charming Snakes with Mesmerizing Memoizers

Turn-in Checklist: For this assignment, there is no paper turn-in required for this problem set. All of your answers should be clearly marked in the charme.py file you modify and submit electronically using https://church.cs.virginia.edu/cs1120/ps7.html. Submit your answers electronically by 11:59pm on Wednesday, 16 November (yes, we mean up until midnight, instead of the normal beginning of class time). If you work with a partner, both partners should submit the same file including both of your email IDs in the authors variable defined at the top of charme.py.

Collaboration Policy - Read Carefully

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.

Purpose

  • Understand how language interpreters work
  • Understand the meta-circular evaluator
  • Ruminate on the nature of universal programming languages
  • Learn how changing the evaluation rules changes programming and running time

Background

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).

Read Chapter 11: Interpreters of the course book.
Download: Download ps7.zip to your machine and extract the charme.py file into your cs1120 directory. This file contains a Python implementation of an interpreter for the Scheme-like language Charme. The implementation closely matches the description in Chapter 11.
For the questions in this assignment, you will modify charme.py which you will submit electronically when you are finished. Please mark clearly (using comments) the changes you have made for Questions 3 - 8.

Getting Started With Charme

In the charme.py window, select Run | Run Module (F5) to load the definitions into the interpreter. This file contains all the code from Chapter 11 of the course book.

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:

>>> initializeGlobalEnvironment()

>> meval('23', globalEnvironment)

23

>>> meval(['+', '1', '2'], globalEnvironment)

3

This is a bit awkward since the first input is not in the standard Charme notation, but the parsed structure.

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:

>>> parse("23")

['23']

>>> parse("(+ 1 2 (* 3 4))")

[['+', '1', '2', ['*', '3', '4']]]

>>> parse("(define square (lambda (x) (* x x)))")

[['define', 'square', ['lambda', ['x'], ['*', 'x', 'x']]]]

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:

>>> meval(parse("(+ 1 2 (* 3 4))")[0], globalEnvironment)

15

It is a bit awkward to remember the [0] 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:

def evalInGlobal(expr):
    return meval(parse(expr)[0], globalEnvironment)
For example:

>>> initializeGlobalEnvironment()

>>> evalInGlobal("(define square (lambda (x) (* x x)))")

>>> evalInGlobal("(square 4)")

16

>>> evalInGlobal("square")

<__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.

Question 1: Explain the result of the last evaluation above, evalInGlobal("square"). (Hint: use str to turn this value into a string.)
Question 2: Define a factorial procedure in Charme. Express your procedure as string in Python by defining a variable called charmeFactorialDefinition. When you evaluate evalInGlobal(charmeFactorialDefinition), it should define a Charme procedure called factorial. Note that the Charme interpreter does not support the full Scheme language.

When you've done it correctly, you should see output like this:

>>> initializeGlobalEnvironment()

>>> evalInGlobal(charmeFactorialDefinition)

>>> evalInGlobal("(factorial 5)")

120

Adding Primitives

The set of primitives provided by our Charme interpreter is sufficient, in that it is enough to express every computation. (One way to prove this would be to implement a Turing Machine simulator in Charme.) However, it is not enough to express every computation in a convenient way.
Question 3: Extend the Charme interpreter by adding a primitive procedure <= to the global environment. You will need to define a procedure that implements the primitive, and modify initializeGlobalEnvironment to install your primitive.

>>> initializeGlobalEnvironment()

>>> evalInGlobal("(<= 5 3)")

False

>>> evalInGlobal("(<= 3 7)")

True

Our Charme interpreter does not provide any primitives for lists. As we saw in Chapter 5 (and Class 7), it is possible to define cons, car and cdr using only the language already defined by Charme. However, it would be more convenient if some primitives for manipulating cons cells and lists are provided.
Question 4: Extend the Charme interpreter by adding primitive procedures cons, car and cdr that behave similarly to the primitive Scheme procedures. (Read on for some hints.)

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))

1

Charme> (cdr (cons 1 2))

2

Charme> quit

Or the equivalent ones with evalInGlobal:

>> evalInGlobal("(cons 1 2)")

(1 . 2)

Question 5: Extend the Charme interpreter by defining the null and null? primitives. (Note that names in Python cannot include question marks, so you will have to use a different name for the Python procedure you use to implement null?.)

Hint: You could use Python's None value to represent null.

Question 6: Extend the Charme interpreter by defining the list primitive procedure. Like the Scheme list primitive procedure, it should take any number of operands and produce as output a list containing each operand as an element in order.

After finishing these questions, you should get the following interactions in the evalLoop():

Charme> (define a (list 1 2 3 4))

Charme> (car a)

1

Charme> (null? a)

False

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 ))

True

Special Forms

The provided Charme interpreter does not include the cond special form. Before attempting to answer the next question, we recommend carefully understanding the evaluation rule for the conditional expression (as described in Section 10.1.2 of the course book, as well Problem Set 3).
Question 7: Extend the Charme interpreter to support the cond special form, with the same meaning as the Scheme cond expression. (Your Charme cond expression does not need to support the special else syntax supported by Scheme.)
After adding cond to your Charme interpreter, you should get the following interactions using evalLoop():

Charme> (cond)

None

Charme> (cond ((> 1 2) 2))

None

Charme> (cond ((> 1 2) 2) ((> 3 2) 3))

3

Charme> (define fibo (lambda (n) (cond ((= n 1) 1) ((= n 2) 1) (true (+ (fibo (- n 1)) (fibo (- n 2)))))))

None

Charme> (fibo 10)

55

Memoizing

Memoizing is a technique for improving efficiency by storing and reusing the results of previous procedure applications. If a procedure has no side effects and uses no global variables, everytime it is evaluated on the same operands it produces the same result.

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.

Question 8: Modify the Charme interpreter to support memoizing for all procedure applications. (Hint: review Python dictionaries from Class 31.)
Once you have modified the interpreter, you should be able to evaluate (fibo 60) without changing the definition of fibo above. By changing the meaning of the application evaluation rule, a procedure that previously had running time exponential in the value of the input, now has running time that is linear in the value of the input!

Question 9: Fill in the missing text for the variables defined for Question 9 with your commands:
  1. pleased = """ 
    What are you most pleased about regarding this course?
    """
  2. displeased = """ 
    What are you most displeased about regarding this course?
    """
  3. hopetolearn = """
    What did you most hope to learn in this course that we have not yet covered?
    """
    
We appreciate your comments on any subject, but especially any concrete suggestions for improving the class and for making the rest of the class as worthwhile as possible.
Question 10: [This question is a "bonus question". It is not necessary to answer this to achive Gold Star level performance on this assignment.]

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 s1
You 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.

Turn-in Reminder: Submit your charme.py file containing all of your answers using https://church.cs.virginia.edu/cs1120/ps7.html.
Print Friendly Print Get a PDF version of this webpage PDF

7 Responses to “Problem Set 7: Charming Snakes with Mesmerizing Memoizers”

  1. Chi Zhang says:
    7 November 2011 at 10:03 pm

    For #3-8, how do you want us to indicate what we’ve changed? can I just have #Question3 to the side?

    Log in to Reply
    • Anonymous says:
      7 November 2011 at 11:58 pm

      You should just add it to the code near the bottom that has already been given to us.

      Log in to Reply
    • cls2be says:
      8 November 2011 at 12:06 am

      I think they just want us to add to the code they have already provided. For example, problem four asks to provide primitive procedures so you would add those procedures to the section labeled “Primitives”.

      Log in to Reply
    • David Evans says:
      8 November 2011 at 1:21 am

      Yes, that’s right. Just put a comment before or beside the code you change/add to modify the interpreter for each question.

      Log in to Reply
      • Chi Zhang says:
        8 November 2011 at 5:21 pm

        Thanks, everyone! :-)

        Log in to Reply
  2. Valerie says:
    13 November 2011 at 1:07 pm

    Hey guys,
    In lobby in rice where we usually have office hours, they are sealing the floor, so we will be meeting on the second floor near the top of the front stairs.

    Log in to Reply
    • Valerie says:
      15 November 2011 at 5:01 pm

      sorry, false alarm, we’ll meet on the first floor. it is open

      Log in to Reply

Leave a Reply Cancel reply

You must be logged in to post a comment.

cs1120 | RSS | Comments RSS | Book | Using These Materials | Login | Admin | Powered by Wordpress