项目说明: https://inst.eecs.berkeley.edu/~cs61a/sp21/proj/scheme/
重新梳理了一下REPL的过程。
- Implementation overview
- Part 0: Testing Your Interpreter
- Part I: The Reader(scheme_reader.py)
- Part II: The Evaluator(scheme.py)
- Problem 2 Frame
- Problem 3 apply
- Problem 4 scheme_eval
- Problem 5 define
- Problem 6 quote
- Problem 7 eval_all
- User-Defined Procedures
- Problem 8 lambda
- Problem 9 define
- Problem 10 make_child_frame
- Problem 11 make_call_frame
- Special Forms
- Problem 12 do_and_form and do_or_form
- Problem 13 do_cond_form
- Problem 14 make_let_frame
- Additional Scheme Tests
- Part III: Write Some Scheme
Implementation overview
-
Read 读取 返回Pair实例
-
scheme_tokens.py 词法分析 lexer
tokenize_lines 函数将scheme表达式分解为token标记,返回一个tokens的Buffer对象
(+ 1 2)
将返回['(', '+', '1', '2', ')']
-
scheme_reader.py 句法分析 parser
将scheme标记解析为python表示
- scheme_read 返回Buffer中的下一个完整表达式
- read_tail 返回Buffer中的的剩余部分
-
-
Eval 计算
- scheme.py → scheme_eval and scheme_apply
- scheme_eval 在给定环境中计算scheme表达式的值
- scheme_apply 将函数Procedure应用到参数上
- scheme.py →
do_?_form
运算特殊表达式,如do_define_form
- scheme.py → scheme_eval and scheme_apply
-
Print 打印
- 打印
__str__
表示
- 打印
-
Loop 循环
- scheme.py → read_eval_print_loop
Part 0: Testing Your Interpreter
tests.scm
包含了很多测试用例,来自课本composingprograms。
加载:python scheme.py tests.scm
Part I: The Reader(scheme_reader.py)
scheme_read和read_tail,传入一个参数src(一个Buffer实例)。
Buffer的两个方法:
src.pop_first()
: 类似python列表的pop(0),从src中删除第一个token并返回。src.current()
: 返回第一个token但不删除。
Buffer对象不能被索引。(buffer[1] ❌)
Problem 1 scheme_read
函数解释:
- Scheme_read 读取完整表达式
- Input: an entire scheme expression
- Format: a Buffer called src
- Reads a complete scheme expression
- 当前token为字符串
nil
,返回nil对象 - 当前token为左括号
(
,说明表达式为Pair或list;对src剩余部分调用read_tail,返回结果。 - 当前token为引号
'
,说明表达式为quote表达式,以Pair形式返回。 - 当前token不是分隔符,说明表达式为原始表达式,返回自身。
- 以上情况均不符合,说明语法错误。
- Read_tail 读取不完整表达式
- Input: the rest of a scheme expression
- Format: a Buffer called src
- Reads until we reach the end of our expression → ')'
- 当前token为None,说明表达式缺少右括号,语法错误
- 当前token为
)
,说明已到达Pair或list的末尾;从Buffer中删除该token,返回nil对象 - 以上情况均不符合,说明下一个token是操作符组合,如
+ 2 3)
。- scheme_read从Buffer中读取下一个完整的表达式,如
+
- 调用read_tail读取组合的剩余部分直到匹配右括号,如
2 3)
- 以Pair实例
Pair(first, second)
的形式返回结果,其中first
- scheme_read从Buffer中读取下一个完整的表达式,如
以(+ 2 3)
为例,先由SR(scheme_read)依次读取完整表达式,再由RT(read_tail)依次返回Pair(first, second)实例。
SR: >> ( + 2 3 ) → Pair('+', Pair(2, Pair(3, nil)))
RT: ( >> + 2 3 ) → Pair('+', Pair(2, Pair(3, nil)))
SR: ( >> + 2 3 ) → '+',读取完整表达式,返回'+'
RT: ( + >> 2 3 ) → Pair(2, Pair(3, nil))
SR: ( + >> 2 3 ) → 2,读取完整表达式,返回2
RT: ( + 2 >> 3 ) → Pair(3, nil),当前token不为None/右括号,返回Pair(first, second)
SR: ( + 2 >> 3 ) → 3,读取完整表达式,返回3,作为上一层的first
RT: ( + 2 3 >> ) → nil,当前token为右括号,到达末尾,返回nil,作为上一层的second
代码:
# Scheme list parser
def scheme_read(src):
"""Read the next expression from SRC, a Buffer of tokens.
>>> scheme_read(Buffer(tokenize_lines(['nil'])))
nil
>>> scheme_read(Buffer(tokenize_lines(['1'])))
1
>>> scheme_read(Buffer(tokenize_lines(['true'])))
True
>>> scheme_read(Buffer(tokenize_lines(['(+ 1 2)'])))
Pair('+', Pair(1, Pair(2, nil)))
"""
if src.current() is None:
raise EOFError
val = src.pop_first() # Get and remove the first token
if val == 'nil':
# BEGIN PROBLEM 1
"*** YOUR CODE HERE ***"
return nil
# END PROBLEM 1
elif val == '(':
# BEGIN PROBLEM 1
"*** YOUR CODE HERE ***"
return read_tail(src)
# END PROBLEM 1
elif val == "'":
# BEGIN PROBLEM 6
"*** YOUR CODE HERE ***"
# END PROBLEM 6
elif val not in DELIMITERS:
return val
else:
raise SyntaxError('unexpected token: {0}'.format(val))
def read_tail(src):
"""Return the remainder of a list in SRC, starting before an element or ).
>>> read_tail(Buffer(tokenize_lines([')'])))
nil
>>> read_tail(Buffer(tokenize_lines(['2 3)'])))
Pair(2, Pair(3, nil))
"""
try:
if src.current() is None:
raise SyntaxError('unexpected end of file')
elif src.current() == ')':
# BEGIN PROBLEM 1
"*** YOUR CODE HERE ***"
src.pop_first()
return nil
# END PROBLEM 1
elif src.current() == '.':
src.pop_first()
expr = scheme_read(src)
if src.current() is None:
raise SyntaxError('unexpected end of file')
if src.pop_first() != ')':
raise SyntaxError('expected one element after .')
return expr
else:
# BEGIN PROBLEM 1
"*** YOUR CODE HERE ***"
first = scheme_read(src)
second = read_tail(src)
return Pair(first, second)
# END PROBLEM 1
except EOFError:
raise SyntaxError('unexpected end of file')
Part II: The Evaluator(scheme.py)
Eval/Apply and Environments:
-
在环境中定义和查找相应的值,环境frame由Frame类创建
-
应用函数procedures
包括内置函数类BuiltinProcedure和自定义函数类LambdaProcedure
-
计算scheme表达式
- scheme_eval和scheme_apply
- do_special_form
Problem 2 Frame
Frame类初始化frame对象时,创建bindings字典用于存放scheme symbol对应的scheme值,parent属性默认为Global Frame。
Frame类包括三个方法:
define(symbol, value):向bindings字典中添加scheme symbol和value的键值对。(P2)
lookup(symbol):在bindings字典中查找symbol对应的value并返回;如果symbol不在当前frame的字典中,向上在parent frame中查找;parent frame中也没有,则抛出SchemeError异常。 (P2)
make_child_frame(formals, vals):以当前frame为parent,创建一个子frame,向其中依次添加formal-val的键值对。(P10)
直接按照define和lookup的说明写代码:
# class Frame
def define(self, symbol, value):
"""Define Scheme SYMBOL to have VALUE."""
# BEGIN PROBLEM 2
"*** YOUR CODE HERE ***"
self.bindings[symbol] = value
# END PROBLEM 2
def lookup(self, symbol):
"""Return the value bound to SYMBOL. Errors if SYMBOL is not found."""
# BEGIN PROBLEM 2
"*** YOUR CODE HERE ***"
if symbol in self.bindings:
return self.bindings[symbol]
elif self.parent:
return self.parent.lookup(symbol)
# END PROBLEM 2
raise SchemeError('unknown identifier: {0}'.format(symbol))
Problem 3 apply
BuiltinProcedure类有两个实例属性:
-
fn是python函数,用于实现scheme的内置函数
-
use_env是一个布尔标志,默认为False,表示是否需要将当前env作为最后一个参数传入到内置函数中。
方法 apply(args, env):传入一个参数列表(Pair对象或nil)和当前环境
- 将Scheme列表转换为python列表
- 如果self.use_env为True,将当前环境env添加到python列表末尾
- 调用self.fn(*arguments_list)并返回结果
代码:
# class BuiltinProcedure
def apply(self, args, env):
"""Apply SELF to ARGS in Frame ENV, where ARGS is a Scheme list (a Pair instance).
>>> env = create_global_frame()
>>> plus = env.bindings['+']
>>> twos = Pair(2, Pair(2, nil))
>>> plus.apply(twos, env)
4
"""
if not scheme_listp(args):
raise SchemeError('arguments are not in a list: {0}'.format(args))
# Convert a Scheme list to a Python list
arguments_list = []
# BEGIN PROBLEM 3
"*** YOUR CODE HERE ***"
while args:
arguments_list.append(args.first)
args = args.rest
if self.use_env:
arguments_list.append(env)
# END PROBLEM 3
try:
return self.fn(*arguments_list)
except TypeError as err:
raise SchemeError('incorrect number of arguments: {0}'.format(self))
Problem 4 scheme_eval
scheme_eval
对所给环境env中的Scheme表达式expr进行运算。
evaluate a call expression:
- Evaluate the operator(Procedure instance)
- Evaluate all of the operands
- Apply the procedure on the evaluated operands, and return the result
在前两步中需要递归调用scheme_eval。
validate_procedure
确认operator确实转换为procedure。- Pair类的map方法,Return a Scheme list after mapping Python function FN to SELF。
scheme_apply
applies a Scheme procedure to a Scheme list of arguments。
- 如果expr.first在SPECIAL_FORMS字典中存在,调用对应值(函数);如果不存在,调用
scheme_eval
获得operator,再调用validate_procedure
判断该operator是否为procedure。 - 对expr.rest的每一项进行运算(
scheme_eval
),返回包含运算后的值的新scheme列表,使用map和lambda。 scheme_apply
def scheme_eval(expr, env, _=None): # Optional third argument is ignored
"""Evaluate Scheme expression EXPR in Frame ENV.
>>> expr = read_line('(+ 2 2)')
>>> expr
Pair('+', Pair(2, Pair(2, nil)))
>>> scheme_eval(expr, create_global_frame())
4
"""
# Evaluate atoms
if scheme_symbolp(expr):
return env.lookup(expr)
elif self_evaluating(expr):
return expr
# All non-atomic expressions are lists (combinations)
if not scheme_listp(expr):
raise SchemeError('malformed list: {0}'.format(repl_str(expr)))
first, rest = expr.first, expr.rest
if scheme_symbolp(first) and first in SPECIAL_FORMS:
return SPECIAL_FORMS[first](rest, env)
else:
# BEGIN PROBLEM 4
"*** YOUR CODE HERE ***"
operator = scheme_eval(first, env)
validate_procedure(operator)
operands = rest.map(lambda x: scheme_eval(x, env))
return scheme_apply(operator, operands, env)
# END PROBLEM 4
Problem 5 define
define
可以定义一个变量,也可以定义一个procedure,根据第一个操作数的类型判断。(此问题只考虑expressions,不考虑procedure)
expressions structure:
Pair(A, Pair(B, nil)), where:
A is the symbol being bound,
B is an expression whose value should be evaluated and bound to A.
This first part evaluates the second operand to obtain a value and binds the first operand, a symbol, to that value. do_define_form
should return the name after performing the binding.
代码:
def do_define_form(expressions, env):
"""Evaluate a define form.
>>> env = create_global_frame()
>>> do_define_form(read_line("(x 2)"), env)
'x'
>>> scheme_eval("x", env)
2
>>> do_define_form(read_line("(x (+ 2 8))"), env)
'x'
>>> scheme_eval("x", env)
10
"""
validate_form(expressions, 2) # Checks that expressions is a list of length at least 2
target = expressions.first
if scheme_symbolp(target):
validate_form(expressions, 2, 2) # Checks that expressions is a list of length exactly 2
# BEGIN PROBLEM 5
"*** YOUR CODE HERE ***"
env.define(target, scheme_eval(expressions.rest.first, env))
return target
# END PROBLEM 5
Problem 6 quote
引用表达式的两种方法:quote
special form 或单引号'.
quote
special form returns its operand expression without evaluating it.
do_quote_form
,返回未运算的操作数。- 完成
scheme_read
(scheme_reader.py),将单引号'后的表达式转换成quote special form。
unlock:
expressions structure:
Pair(A, nil), where:
A is the quoted expression
>>> expr = Pair(Pair('+', Pair('x', Pair(2, nil))), nil)
>>> do_quote_form(expr, global_frame) # Make sure to use Pair notation
? Pair('+', Pair('x', Pair(2, nil)))
-- OK! --
代码:
# scheme.py
def do_quote_form(expressions, env):
"""Evaluate a quote form.
>>> env = create_global_frame()
>>> do_quote_form(read_line("((+ x 2))"), env)
Pair('+', Pair('x', Pair(2, nil)))
"""
validate_form(expressions, 1, 1)
# BEGIN PROBLEM 6
"*** YOUR CODE HERE ***"
return expressions.first
# END PROBLEM 6
# scheme_read.py
def scheme_read(src):
"""Read the next expression from SRC, a Buffer of tokens.
>>> scheme_read(Buffer(tokenize_lines(['nil'])))
nil
>>> scheme_read(Buffer(tokenize_lines(['1'])))
1
>>> scheme_read(Buffer(tokenize_lines(['true'])))
True
>>> scheme_read(Buffer(tokenize_lines(['(+ 1 2)'])))
Pair('+', Pair(1, Pair(2, nil)))
"""
if src.current() is None:
raise EOFError
val = src.pop_first() # Get and remove the first token
if val == 'nil':
# BEGIN PROBLEM 1
"*** YOUR CODE HERE ***"
return nil
# END PROBLEM 1
elif val == '(':
# BEGIN PROBLEM 1
"*** YOUR CODE HERE ***"
return read_tail(src)
# END PROBLEM 1
elif val == "'":
# BEGIN PROBLEM 6
"*** YOUR CODE HERE ***"
return Pair('quote', Pair(scheme_read(src), nil))
# END PROBLEM 6
elif val not in DELIMITERS:
return val
else:
raise SyntaxError('unexpected token: {0}'.format(val))
Problem 7 eval_all
Change the eval_all
function in scheme.py
(which is called from do_begin_form
) to complete the implementation of the begin special form. A begin
expression is evaluated by evaluating all sub-expressions in order. The value of the begin
expression is the value of the final sub-expression.(begin)
- expressions为nil时, 返回None
- 依次运算(scheme_eval)子表达式
- 返回最后一个子表达式的值
def eval_all(expressions, env):
"""Evaluate each expression in the Scheme list EXPRESSIONS in
Frame ENV (the current environment) and return the value of the last.
>>> eval_all(read_line("(1)"), create_global_frame())
1
>>> eval_all(read_line("(1 2)"), create_global_frame())
2
>>> x = eval_all(read_line("((print 1) 2)"), create_global_frame())
1
>>> x
2
>>> eval_all(read_line("((define x 2) x)"), create_global_frame())
2
"""
# BEGIN PROBLEM 7
# return scheme_eval(expressions.first, env) # replace this with lines of your own code
if expressions is nil:
return None
res = nil
while expressions is not nil:
res = scheme_eval(expressions.first, env)
expressions = expressions.rest
return res
# END PROBLEM 7
User-Defined Procedures
LambdaProcedure
类的实例,有三个实例属性:
formals
is a Scheme list of the formal parameters (symbols) that name the arguments of the procedure.body
is a Scheme list of expressions; the body of the procedure.env
is the environment in which the procedure was defined.
Problem 8 lambda
do_lambda_form
creates and returns a LambdaProcedure instance.
unlock:
(lambda (a b c) (+ a b c))
Pair('a', Pair('b', Pair('c', nil))) # formals
Pair(Pair('+', Pair('a', Pair('b', Pair('c', nil)))), nil) # body
code:
def do_lambda_form(expressions, env):
"""Evaluate a lambda form.
>>> env = create_global_frame()
>>> do_lambda_form(read_line("((x) (+ x 2))"), env)
LambdaProcedure(Pair('x', nil), Pair(Pair('+', Pair('x', Pair(2, nil))), nil), <Global Frame>)
"""
validate_form(expressions, 2)
formals = expressions.first
validate_formals(formals)
# BEGIN PROBLEM 8
"*** YOUR CODE HERE ***"
return LambdaProcedure(formals, expressions.rest, env)
# END PROBLEM 8
Problem 9 define
调整problem 5中的do_define_form
,以便正确处理procedure定义中的简写形式。
- Using the given variables
target
andexpressions
, find the defined function's name(target.first), formals(target.rest), and body(expressions.rest). - Create a
LambdaProcedure
instance using the formals and body. Hint: You can use what you've done in Problem 8 and calldo_lambda_form
on the appropriate arguments - Bind the name to the
LambdaProcedure
instance(env.define)
code:
def do_define_form(expressions, env):
"""Evaluate a define form.
>>> env = create_global_frame()
>>> do_define_form(read_line("(x 2)"), env)
'x'
>>> scheme_eval("x", env)
2
>>> do_define_form(read_line("(x (+ 2 8))"), env)
'x'
>>> scheme_eval("x", env)
10
>>> # problem 9
>>> env = create_global_frame()
>>> do_define_form(read_line("((f x) (+ x 2))"), env)
'f'
>>> scheme_eval(read_line("(f 3)"), env)
5
"""
validate_form(expressions, 2) # Checks that expressions is a list of length at least 2
target = expressions.first
if scheme_symbolp(target):
validate_form(expressions, 2, 2) # Checks that expressions is a list of length exactly 2
# BEGIN PROBLEM 5
"*** YOUR CODE HERE ***"
env.define(target, scheme_eval(expressions.rest.first, env))
return target
# END PROBLEM 5
elif isinstance(target, Pair) and scheme_symbolp(target.first):
# BEGIN PROBLEM 9
"*** YOUR CODE HERE ***"
procedure = do_lambda_form(Pair(target.rest, expressions.rest), env)
env.define(target.first, procedure)
return target.first
# END PROBLEM 9
else:
bad_target = target.first if isinstance(target, Pair) else target
raise SchemeError('non-symbol: {0}'.format(bad_target))
Problem 10 make_child_frame
- class:
Frame
- method:
make_child_frame
- arguments:
formals
,vals
- return: a new child frame, binding the formal parameters to the values
- 如果参数值的个数与形参个数不匹配,raise SchemeError
- 创建一个新的Frame实例,它的
parent
是self
- 在2创建的frame中,将每个形参与参数值绑定。
- 返回新的frame。
code:
def make_child_frame(self, formals, vals):
"""Return a new local frame whose parent is SELF, in which the symbols
in a Scheme list of formal parameters FORMALS are bound to the Scheme
values in the Scheme list VALS. Both FORMALS and VALS are represented as Pairs.
Raise an error if too many or too few
vals are given.
>>> env = create_global_frame()
>>> formals, expressions = read_line('(a b c)'), read_line('(1 2 3)')
>>> env.make_child_frame(formals, expressions)
<{a: 1, b: 2, c: 3} -> <Global Frame>>
"""
if len(formals) != len(vals):
raise SchemeError('Incorrect number of arguments to function call')
# BEGIN PROBLEM 10
"*** YOUR CODE HERE ***"
frame = Frame(self)
while formals is not nil:
frame.define(formals.first, vals.first)
formals = formals.rest
vals = vals.rest
return frame
# END PROBLEM 10
Problem 11 make_call_frame
- class:
LambdaProcedure
- method:
make_call_frame
- arguments:
args
,env
- return: a new frame
- use:
make_child_frame
(self.env)
code:
# class LambdaProcedure
def make_call_frame(self, args, env):
"""Make a frame that binds my formal parameters to ARGS, a Scheme list
of values, for a lexically-scoped call evaluated in Frame ENV, the environment."""
# BEGIN PROBLEM 11
"*** YOUR CODE HERE ***"
return self.env.make_child_frame(self.formals, args)
# END PROBLEM 11
Special Forms
可参考do_if_form
Problem 12 do_and_form
and do_or_form
and
和or
(注意短路特性):
Provided: is_true_primitive
, is_false_primitive
code:
def do_and_form(expressions, env):
"""Evaluate a (short-circuited) and form.
>>> env = create_global_frame()
>>> do_and_form(read_line("(#f (print 1))"), env)
False
>>> do_and_form(read_line("((print 1) (print 2) (print 3) (print 4) 3 #f)"), env)
1
2
3
4
False
"""
# BEGIN PROBLEM 12
"*** YOUR CODE HERE ***"
if expressions is nil:
return True
while expressions is not nil:
last_exp = scheme_eval(expressions.first, env)
if is_false_primitive(last_exp):
return False
else:
expressions = expressions.rest
return last_exp
# END PROBLEM 12
def do_or_form(expressions, env):
"""Evaluate a (short-circuited) or form.
>>> env = create_global_frame()
>>> do_or_form(read_line("(10 (print 1))"), env)
10
>>> do_or_form(read_line("(#f 2 3 #t #f)"), env)
2
>>> do_or_form(read_line("((begin (print 1) #f) (begin (print 2) #f) 6 (begin (print 3) 7))"), env)
1
2
6
"""
# BEGIN PROBLEM 12
"*** YOUR CODE HERE ***"
while expressions is not nil:
first_exp = scheme_eval(expressions.first, env)
if is_true_primitive(first_exp):
return first_exp
else:
expressions = expressions.rest
return False
# END PROBLEM 12
Problem 13 do_cond_form
cond
predicate为true时:
- 没有对应的结果子表达式,返回predicate的值
- 有多个结果子表达式,返回最后一个子表达式的值(
eval_all
)
code:
def do_cond_form(expressions, env):
"""Evaluate a cond form.
>>> do_cond_form(read_line("((#f (print 2)) (#t 3))"), create_global_frame())
3
"""
while expressions is not nil:
clause = expressions.first
validate_form(clause, 1)
if clause.first == 'else':
test = True
if expressions.rest != nil:
raise SchemeError('else must be last')
else:
test = scheme_eval(clause.first, env)
if is_true_primitive(test):
# BEGIN PROBLEM 13
"*** YOUR CODE HERE ***"
if clause.rest is nil:
return test
return eval_all(clause.rest, env)
# END PROBLEM 13
expressions = expressions.rest
Problem 14 make_let_frame
function: 'make_let_frame'
return: a child frame of env
argument: bindings
use: validate_form
, validate_formals
注意:每个binding的frame不互通,binding1中的name1并不能在binding2中被调用,但是能在body
中使用。通过Pair创建names和values的scheme列表,validate_form(binding, 2, 2)确保binding格式为symbol + expression.
validate_formals(names)确保names为scheme列表且各name不重复。
code:
def make_let_frame(bindings, env):
"""Create a child frame of Frame ENV that contains the definitions given in
BINDINGS. The Scheme list BINDINGS must have the form of a proper bindings
list in a let expression: each item must be a list containing a symbol
and a Scheme expression."""
if not scheme_listp(bindings):
raise SchemeError('bad bindings list in let form')
names = values = nil
# BEGIN PROBLEM 14
"*** YOUR CODE HERE ***"
while bindings is not nil:
binding = bindings.first
validate_form(binding, 2, 2)
names = Pair(binding.first, names)
values = Pair(scheme_eval(binding.rest.first, env), values)
bindings = bindings.rest
validate_formals(names)
# END PROBLEM 14
return env.make_child_frame(names, values)
Additional Scheme Tests
Part III: Write Some Scheme
Problem 15 enumerate
类似python enumerate, 由于需要计数,用辅助函数传入count=0.
code:
(define (enumerate s)
; BEGIN PROBLEM 15
(define (enumerate2 s count)
(if (null? s)
nil
(cons (list count (car s)) (enumerate2 (cdr s) (+ count 1)))))
(enumerate2 s 0)
)
Problem 16 merge
依次用comp比较两列表第一个值的大小
(define (merge comp list1 list2)
; BEGIN PROBLEM 16
(cond
((eqv? list1 nil) list2)
((eqv? list2 nil) list1)
((comp (car list1) (car list2))
(cons (car list1)
(merge comp (cdr list1) list2)))
(else
(cons (car list2)
(merge comp list1 (cdr list2)))))
)
Problem 17 let-to-lambda
类似scheme_eval:
当expr为atom或quoted时,直接返回expr;
当expr为lambda或define form时,需要考虑body中可能出现的let form;
当expr为let form时,将let expr中的形参zip并作为lambda的形式参数,let expr中的参数值zip传入lambda函数,let expr中的body作为lambda函数体;
其他情况,将其作为define/lambda/let的函数体进行递归调用。
(define (zip pairs)
(list (map car pairs) (map cadr pairs)))
;; Converts all let special forms in EXPR into equivalent forms using lambda
(define (let-to-lambda expr)
(cond ((atom? expr)
; BEGIN PROBLEM 17
expr
; END PROBLEM 17
)
((quoted? expr)
; BEGIN PROBLEM 17
expr
; END PROBLEM 17
)
((or (lambda? expr)
(define? expr))
(let ((form (car expr))
(params (cadr expr))
(body (cddr expr)))
; BEGIN PROBLEM 17
(cons form (cons (map let-to-lambda params) (map let-to-lambda body)))
; END PROBLEM 17
))
((let? expr)
(let ((values (cadr expr))
(body (cddr expr)))
; BEGIN PROBLEM 17
(cons (cons 'lambda
(cons (car (zip (let-to-lambda values))) (let-to-lambda body)))
(cadr (zip (let-to-lambda values))))
; END PROBLEM 17
))
(else
; BEGIN PROBLEM 17
(map let-to-lambda expr)
; END PROBLEM 17
)))