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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM