日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

大家好,日拱一卒,我是梁唐。本文始发于公众号Coder梁

我们继续来肝伯克利CS61A的scheme project,这是这个project的第三篇,如果漏掉了之前的建议先去补一下。

课程链接

项目原始文档

Github

在上一测试手机是否被监控变量名文章当中,我们进一步完善了scheme解释器的功能,让它能够支持自定义函数等许多功能。在这github开放私库一篇文章当中,我们要继续完善功能,让我们的解释器能够支持更多语法。

Special Forms

整个项目到这里我们已经完成了基础的核心部分,接下来还要处理一些特殊的类型。

我们还有很多语法没有支持,比如逻辑运算符if,and, or, cond这些。这些表达式使用频率非常高,也非常重要。

在sche测试用例me当中,只有False才是假,所有其他的值都会被视为真,包括0和nil。你可以使用scheme_primitive.py文件中的scheme_truepscheme_fal测试手机是否被监控sep函数来判断一个值是测试true还是false

注意:scheme传统gitlab中使用#f来表示false#t测试用例表示true。在我们的解释器当中true, True, #t均视为等github官网登陆入口价。然而在解锁测试数据时,需要使用#t#f

我们提供了if语句的实现,do_python保留字if_form函数作为参考。确保你已经理解了if语句的原理,再进行下面的python安装教程题目。

Problem 13

实现dGito_and_forpython语言m变量泵do_or_form函数来完成andor表达式的支持。

对于and语句来说,你需要从左往右evaluate所有的表达式,如果遇到结果是false,那么变量的定义返回变量值#f。否则返回最后python基础教程一个子语句的结果。如果输入的语句为空,也返回#t

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

对于or语句来说,我们也需要从左往右评估每一个子语句的值。如果某一个子语句的结果是true,直接返回。否则返回#f。如果输入为空,也返回#f

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

在开发之前,先答题解锁测试:

python3 ok -q 13 -u

开发之后,测试:

python3 ok -q 13

答案

这两个函github下载数逻辑并不复杂,使用递归很容易搞定。

def do_and_form(expressions, env):
    """Evaluate a (short-circuited) and form."""
    # BEGIN PROBLEM 13
    "*** YOUR CODE HERE ***"
    if expressions is nil:
        return True
    elif expressions.second is nil:  # Tail context
        return scheme_eval(expressions.first, env)
    else:
        first_expr = scheme_eval(expressions.first, env)
        if scheme_falsep(first_expr):  # The first expression is False
            return False
        elif scheme_truep(first_expr):  # The first expression is True
            return do_and_form(expressions.second, env)
    # END PROBLEM 13
def do_or_form(expressions, env):
    """Evaluate a (short-circuited) or form."""
    # BEGIN PROBLEM 13
    "*** YOUR CODE HERE ***"
    if expressions is nil:
        return False
    elif expressions.second is nil:  # Tail context
        return scheme_eval(expressions.first, env)
    else:
        first_expr = scheme_eval(expressions.first, env)
        if scheme_falsep(first_expr):  # The first expression is False
            return do_or_form(expressions.second, env)
        else:  # The first expression is True
            return first_expr
    # END PROBLEM 13

Problem 1github汤姆4

完善do_cond_form函数,让他能够返python语言回第一个为true的子语句的值,如果都为f变量与函数alse,返回else语句的值。

然而存在一些特殊情况:

  • 当判断为true的值没有对应的返回结果,那么返回该值
  • cond语句的某一个分支中存在多个结果语句时,返回最后一个,提示,可以使用eval_all函数

你的代码需要能通过下列测试数据:

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

如果cgithub官网ond语句既没有为true的分支也没有else语句,那么返回None

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

编码之前,先回答问题变量值解锁测试:

python3 ok -q 14 -u

编码之后,进行测试:

python3 ok -q 14

答案

整个函数的框架已经搭好github官网登陆入口了,函github下载数内部使用了while循环来遍历所变量值有的表达式,并且实现了else语句的部分。

test表示当前的语句是否是trugithube,如果是真,那么返回test之后的内容。但由于题目中说了,有些情况只有判断条件,没有返回python123平台登录结果。这个时候就要返回判断条件的结果本身,也就是test。当然也有可能表达测试用例式有多个,这种情况使用eval_all函数返回最后一个表达式的结果即测试工程师可。

如果test为假则继续往下执行其他条件。

def do_cond_form(expressions, env):
    """Evaluate a cond form."""
    while expressions is not nil:
        clause = expressions.first
        check_form(clause, 1)
        if clause.first == 'else':
            test = True
            if expressions.second != nil:
                raise SchemeError('else must be last')
        else:
            test = scheme_eval(clause.first, env)
        if scheme_truep(test):
            # BEGIN PROBLEM 14
            "*** YOUR CODE HERE ***"
            if clause.second == nil:
                return test
            return eval_all(clause.second, env)
            # END PROBLEM 14
        expressions = expressions.second

Problem 15

let特殊类型,它会在本地绑定symbol和value,但是这个绑定只会在原地变量泵生效,比如这个例子:

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

(let ((x 42) (y (* x 10))) (list x y))语句当中,github官网绑定y时用到的x是全局的值,也就是5,而不是刚刚绑定的42。因为绑定值的操作是变量与函数最后一起执行的,xy是一起绑定的。有一点类似于以下测试手机是否被监控Python代码:

x = 5
x, y = 42, x * 10

实现makepython可以做什么工作_let_frame方法,它会返回一个当前env的子frame,在这个frame当中,将symbol绑定到对应的value上。bindings scheme list包含一系列pairs,其中每一个包含一个symbol和对应的表达式。

你可能会用到下面GitHub这些函数:

  • check_form:这个函数用来检查binding结构
  • check变量是什么意思_formals:这python编程个函数检查测试抑郁程度的问卷formal测试抑郁症的20道题数,确保每一个symbol都是不同的
  • make_child_frame:这个函数(你在11题中开发变量值的),它接收一个Pair表示formal,一个Pair表示va测试工程师lue。返回一个绑定了gitlabformal和vgiti轮胎alpython123平台登录ue的测试抑郁程度的问卷fr测试ame

在你编码之前,先python可以做什么工作回答问题解锁测试样例:

python3 ok -q 15 -u

编码之测试你的自卑程度后,进行测试:

python3 ok -q 15

答案

只要搞清楚了let语句是先评估,评估完了之后一起绑定的,这题就算是搞定了一大半python语言

但坑爹的是,原文的题意当中没有明确地指出这一点,这是要我们基于老师给的问题和一些描述去自己分析和推理的。所以我当时做这题的时候,还是费了一些周折。

def make_let_frame(bindings, env):
    """Create a child frame of 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')
    # BEGIN PROBLEM 15
    "*** YOUR CODE HERE ***"
    formals, args = nil, nil
    while bindings is not nil:
        check_form(bindings.first, 2, 2)
        formals = Pair(bindings.first.first, formals)
        args = Pair(scheme_eval(bindings.first.second.first, env), args)
        bindings = bindings.second
    check_formals(formals)
    return env.make_child_frame(formals, args)
    # END PROBLEM 15

P测试英文roblem 16

实现do_mu_form函数来评估特殊类型github汤姆mumu不是一种标准的scheme表达式类型。

mu表达式类似于lambda表达式,但不同的是,当evaluate mu语句时,它是动态scope的。lambda表达式是静态scope,因为lambda表达式执行的时候,使用的envlambda创建时的frame。而mu使用的是表达式执行时的frame,因此是动态的。

mu表达式通过MuProcedure类实现,大部分变量代码github中文社区已经开发好了。

完善MuProcedure类,当它被调用的时候,它依赖的外部frgithubame是动态的,也python123平台登录正因此,MuProcedurgithub直播平台永久回家e类中不需要存储framepython基础教程例。

下面是一个mu表达式的例子:

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(三)

编码之前,先回答问题确保理解正确:

python3 ok -q 16 -u

编码完成之后,进行测试:

python3 ok -q 16

答案

我们看下m测试仪u表达式的使用方法,其实它和lambda的语法是一样的。所以do_mu_formdo_lambda_form的逻辑也是一样的。

def do_mu_form(expressions, env):
    """Evaluate a mu form."""
    check_form(expressions, 2)
    formals = expressions.first
    check_formals(formals)
    # BEGIN PROBLEM 16
    "*** YOUR CODE HERE ***"
    return MuProcedure(formals, expressions.second)
    # END PROBLEM 16

唯一的区别就是运行时的envlambda是静态的,而mu是动态的。

看起来好像很复杂,但仔细思考会发现这两个在实现里的区别其实很小。只有创建子frame的时候有一些区别,lambda表达式创建运行时的frame用的是自身实例中存储的env,这样就能保证不论什么时候运行lambda,使用的都是创建时的frame

mu是动态的,从类的定义中我们也可以看出来,它没github官网登陆入口env这个实例属性。那么我们在创建子frame的时候用的就不是本身存储的env,而是外界传入的。外界传入的github中文官网网页env是在运行时传入的,这样就可以保证了,mu运行时依赖的外部变量测试英文是动态的。

代码只有一行,但理清楚这里面的逻辑却不简单python可以做什么工作

class MuProcedure(Procedure):
    """A procedure defined by a mu expression, which has dynamic scope.
     _________________
    < Scheme is cool! >
     -----------------
               ^__^
               (oo)_______
                (__)       )/
                    ||----w |
                    ||     ||
    """
    def __init__(self, formals, body):
        """A procedure with formal parameter list FORMALS (a Scheme list) and
        Scheme list BODY as its definition."""
        self.formals = formals
        self.body = body
    # BEGIN PROBLEM 16
    "*** YOUR CODE HERE ***"
    def make_call_frame(self, args, env):
        return env.make_child_frame(self.formals, args)
    # END PROBLEM 16
    def __str__(self):
        return str(Pair('mu', Pair(self.formals, self.body)))
    def __repr__(self):
        return 'MuProcedure({0}, {1})'.format(
            repr(self.formals), repr(self.body))

到这里,整个scheme的解释器就算是完成了。

但解释器除了要支持解释执行scheme代码之外,其实还有很多其他的内容。在附加题当中,老师给变量的定义我们留了两个非常困难的问题:尾递归和尾递归的运行优化。

这部分内容比较困难,而且涉及github到更多的知识,因此我们单独成篇,放在之后的文章当中和大家分享。

发表评论

提供最优质的资源集合

立即查看 了解详情