[2021 spring] CS61A Project 4: Scheme Interpreter

项目说明: https://inst.eecs.berkeley.edu/~cs61a/sp21/proj/scheme/
重新梳理了一下REPL的过程。

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
  • 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
  1. 当前token为字符串nil,返回nil对象
  2. 当前token为左括号 (,说明表达式为Pair或list;对src剩余部分调用read_tail,返回结果。
  3. 当前token为引号',说明表达式为quote表达式,以Pair形式返回。
  4. 当前token不是分隔符,说明表达式为原始表达式,返回自身。
  5. 以上情况均不符合,说明语法错误。
  • Read_tail 读取不完整表达式
    • Input: the rest of a scheme expression
    • Format: a Buffer called src
    • Reads until we reach the end of our expression → ')'
  1. 当前token为None,说明表达式缺少右括号,语法错误
  2. 当前token为),说明已到达Pair或list的末尾;从Buffer中删除该token,返回nil对象
  3. 以上情况均不符合,说明下一个token是操作符组合,如+ 2 3)
    1. scheme_read从Buffer中读取下一个完整的表达式,如+
    2. 调用read_tail读取组合的剩余部分直到匹配右括号,如2 3)
    3. 以Pair实例Pair(first, second)的形式返回结果,其中first

(+ 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类有两个实例属性:

  1. fn是python函数,用于实现scheme的内置函数

  2. use_env是一个布尔标志,默认为False,表示是否需要将当前env作为最后一个参数传入到内置函数中。

方法 apply(args, env):传入一个参数列表(Pair对象或nil)和当前环境

  1. 将Scheme列表转换为python列表
  2. 如果self.use_env为True,将当前环境env添加到python列表末尾
  3. 调用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:

  1. Evaluate the operator(Procedure instance)
  2. Evaluate all of the operands
  3. 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。
  1. 如果expr.first在SPECIAL_FORMS字典中存在,调用对应值(函数);如果不存在,调用scheme_eval获得operator,再调用validate_procedure判断该operator是否为procedure。
  2. 对expr.rest的每一项进行运算(scheme_eval),返回包含运算后的值的新scheme列表,使用map和lambda。
  3. 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.

  1. do_quote_form,返回未运算的操作数。
  2. 完成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)

  1. expressions为nil时, 返回None
  2. 依次运算(scheme_eval)子表达式
  3. 返回最后一个子表达式的值
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定义中的简写形式。

  1. Using the given variables target and expressions, find the defined function's name(target.first), formals(target.rest), and body(expressions.rest).
  2. Create a LambdaProcedure instance using the formals and body. Hint: You can use what you've done in Problem 8 and call do_lambda_form on the appropriate arguments
  3. 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
  1. 如果参数值的个数与形参个数不匹配,raise SchemeError
  2. 创建一个新的Frame实例,它的parentself
  3. 在2创建的frame中,将每个形参与参数值绑定。
  4. 返回新的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

andor(注意短路特性):

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

let

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
         )))
posted @ 2021-07-26 23:33  ikventure  阅读(13501)  评论(1编辑  收藏  举报