In part 2 we will be implementing the code for procedures. By the end of this, you will be able to interpret user-defined functions, lambda functions, and something special called “Mu-procedures” that you will soon learn to dislike. This part will be handling problems 6-11. Good luck.
Problem 6: “Begin”
In Scheme, we have the ‘begin’ special form. This lets us evaluate through a series of expressions in scheme and return only the value of the last expression.
It is useful because it allows for side effects from the first expressions to take place while returning only the evaluated value of the last expression in the series. For examples, see the examples in the CS 111 instructions and for even more understanding, read the short documentation for MIT/GNU’s implementation of scheme. Note from the documentation, that writing begin is often pointless because many special forms already apply it implicitly.
What you need to do, is implement the eval_all() function in scheme_eval_apply.py. Eval_all() takes in two arguments: expressions and env.
Scheme
(1 2 3)
Python
expressions = Pair(1, Pair(2, Pair(3, nil)))
You should go through expressions recursively, evaluating each one using scheme_eval(). When you get to the last expression, evaluate it like the others, but also return its value.
Problem 7: Lambda Procedures
We need to implement the do_lambda_form() function in scheme_forms.py. This function takes in two arguments: expressions and env.
expressions is going to be objects of class Pair representing our formals (just operators for the lambda function) and the second part is the operations.
The variable formals is given to us: formals = expressions.first
Scheme ———————
(lambda (x y) (+x y))
Python ———————–
expressions = Pair(Pair(‘x’, Pair(‘y’, nil)), Pair(Pair(‘+’, Pair(‘x’, Pair(‘y’, nil))), nil))
formals = Pair(‘x’, Pair(‘y’, nil)) #These are the arguments/operators to the lambda func
expressions.rest = Pair(Pair(‘+’, Pair(‘x’, Pair(‘y’, nil))), nil) # the operations
What we need to do is take the given information and return a LambdaProcedure object. The LambdaProcedure class is defined in scheme_classes.py. It would be very helpful to review it because you need to initialize and return an object from it.
Hint: LambdaProcedure Class
The code you write for this problem should be a single line long
Problem 8: Making New Frames
In this problem we will be dealing with frames and the creation thereof. When we make a frame, we are initializing an object of class Frame. Each frame has bindings (a dict representing all the variables and values within a frame) and parent (the frame from which the new frame comes from).
In Problem 1 we made the define() method that allowed us to define variables by saving them into the instance variable bindings. ! Remember this !
For Problem 8, we are implementing the class method make_child_frame() which takes in two arguments: formals and vals.
formals is a list of the symbols (the variables to which we are assigning values)
vals is a list of the values we are assigning to the symbols in formals
Scheme
(define a 1)
(define b 2)
(define c 3)
Python
formals = Pair(‘a’, Pair(‘b’, Pair(‘c’, nil)))
vals = Pair(‘a’, Pair(‘b’, Pair(‘c’, nil)))
The CS 111 website gives four steps which are decently clear and pretty much make up the pseudo code for this problem:
Hint: Assigning the Values
You somehow need to iterate through each of the lists and get the values at each point to be bound to one another. Personally, I wrote a function that converted linked lists into simple python lists and then I iterated through the python lists because that was easiest to me. There are multiple and probably better ways to do this.
Hint: Raise Error Syntax
raise Error(“Custom Error Message”)
And for even more, but less related help
Problem 9: Lambda meets Frame
This is another problem that should not require a lot of code, only two lines to be exact. In scheme_eval_apply.py we have the scheme_apply() function that applies procedures to arguments.
scheme_apply() takes in procedure, args, and env. procedure is going to be an object of class Procedure. It could be a BuiltinProcedure or a LambdaProcedure (MuProcedure or MacroProcedure later too).
procedure = LambdaProcedure(operators, operations, env)
LambdaProcedure(Pair(‘x’, nil), Pair(Pair(‘*’, Pair(‘x’, Pair(‘x’, nil))), nil), <Global Frame>)
args stores the values of the arguments that our procedure is going to be applied to. In the above box it would be the value that replaces the x’s in the LambdaProcedure.
Scheme
(define square (lambda (x) (* x x)))
(square 21)
Python
args = Pair(21, nil)
When we make a call to a lambda function, it opens a new frame in which the operators are defined and the operations take place. That means you need to make a new frame whose parent is the current frame. (First Line) Then you need to return an evaluation of all the body of the lambda function (the operations part) using eval_all(). (Second Line)
Hint: SchemeError: unknown identifier: y
Recall from the first box above in Problem 9, that an object of LambdaProcedure is initialized by taking in the current environment (env) and makes an instance variable: self.env. An unkown identifier error will occur if we are not creating the new frame based off the current frame self.env.
How can you call the make_child_frame() method from the LambdaProcedure‘s instance variable self.env instead of just from env? ๐คท
Hint: Making New Frames
In problem 8 we implemented the method of class Frame make_child_frame(formals, vals)
Unfortunately there is no more help ):
Problem 10: Defining Func’s Shorthandedly
In Problem 10 we are going to edit the do_define_form() function that we wrote a part of in Problem 4. In Problem 4 we made it so that we could assign values to a variable name. Now we want to make it so we can assign functions to a variable name using the following syntax in Scheme:
scm> (define (square x) (* x x))
square
scm> (square 2)
4
do_define_form() takes two arguments: expressions and env. expressions, as normal, is a list of objects from the class Pair. Consider the following example:
Scheme
(define (f x y) (+ x y))
Python
expressions =
Pair(Pair(‘f’, Pair(‘x’, Pair(‘y’, nil))), Pair(Pair(‘+’, Pair(‘x’, Pair(‘y’, nil))), nil))
You need to dissect expressions to get the function name, the operators, and the operations. Keep in mind we have the variable signature which is equal to the red highlighted portion of expressions above. To define the function we need to take the variable name and assign an object of class LambdaProcedure to it. To create this object we are going to use the function do_lambda_form() from Problem 7.
As a reminder, do_lambda_form() takes in two arguments: expressions and env. It returns an object of class LambdaProcedure. expressions must be a scheme list represented by Pair class objects that hold the operators and the operations. You must create this Pair object in which the first part is the operators and the rest is the operations.
Once you have the LambdaProcedure object, assign it to the variable name in the current frame.
Problem 11: The ‘Mu’
The first important thing is to understand the difference between lexical and dynamic scoping. Here is the explanation again from the CS 111 website:
First, implement do_mu_form() in scheme_forms.py so that it returns an object of class MuProcedure. This should probably only be one line long. If you need help just look at your code for Problem 7. We are essentially doing the same thing except we are ignoring env because when initializing a MuProcedure we ignore the current frame because of dynamic scoping.
Make sure to look at the __init__() for MuProcedure to see what it intakes as arguments:
Here are what the arguments to the __init__() would look like if we were initializing a MuProcedure object based off of the given scheme expression:
Scheme
(define f (mu (x) (+ x y)))
Python
formals = Pair(‘x’, nil) #variable name
body = Pair(Pair(‘+’, Pair(‘x’, Pair(‘y’, nil))), nil) #operations
Second, you need to edit the scheme_apply() function in scheme_eval_apply.py for when the procedure is a MuProcedure. Return the evaluation of the body of the MuProcedure object, but remember you have to do that using the correct frame that results in dynamic scoping. So beforehand make a new frame based off of the current frame. Use this new frame when you evaluate the MuProcedure.
Hint: Evaluating a Procedure
In Problem 6 we implemented the eval_all() function which we have been using for evaluating procedures. It intakes expressions and env.
eval_all(expressions, env)
Hint: Creating a New Frame
Review Problem 8 in which we implemented the method make_child_frame() of class Frame
</end>
Hopefully this was a sufficient help. Please submit any corrections or questions in the comments below. I will try my best to answer and update the article accordingly.