項目說明: 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
)))