问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

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

发布网友 发布时间:2024-10-03 08:47

我来回答

1个回答

热心网友 时间:2024-10-08 01:19

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

我们来继续肝伯克利CS61A的scheme project,这是本project的第四篇,如果漏掉了之前的内容,可以去翻一下历史记录。

课程链接

项目原始文档

Github

在上一篇文章当中,我们完整实现了scheme解释器的功能,在这一篇文章中,我们用我们刚刚自己开发的解释器来做几个问题。

我们可以注意到,不仅scheme解释器本身是一个树递归的程序,并且它在处理其他递归问题上非常灵活。你将在questions.scm文件当中实现接下来的几个问题

虽然你已经完成了scheme解释器的开发,但由于可能存在潜在的bug。所以建议先使用通用的sheme解释器实现之后,再使用刚开发出的解释器进行测试,从而确保你的代码能够正常运行。

Problem 17

实现enumerate过程,它接收一个list,返回一个二元list。

这个二元list当中的每个元素是下标和值得组合,如:

开发完成之后,进行测试:

python3 ok -q 17答案

lisp当中也有循环的语法,如果使用循环会简单很多。但老师讲课的内容当中没包括循环,所以我们还是只能使用递归来进行处理。

如果要递归处理,必然会发现一个问题,就是enumerate函数的入参只有一个list,而输出要带上下标。但问题是,我们在递归的时候拿不到当前下标这个变量。所以进而可以想到,只有一个参数递归肯定是解决不了的,我们至少需要两个参数。

在不改动原有函数签名的情况下,唯一的办法就是使用高阶函数。在函数内部再定义一个函数,然后我们再调用这个函数。

递归的逻辑其实不难,可以参考一下代码,就不过多赘述了。

(define (enumerate s); BEGIN PROBLEM 17(define (enum-iter n s)(if (null? s) nil(cons(list n (car s))(enum-iter (+ n 1) (cdr s)))))(enum-iter 0 s))Problem 18

实现list-change过程,给定total和denominations,total表示总额,denominations表示我们有的面值,面值按照降序排序。我们要返回能够组成总面值total的所有方式。

比如total是10,denominations是(25, 10, 5, 1),那么答案是:

再比如total是5,denominations是(4, 3, 2, 1),答案是:

在实现的过程当中,你需要用到一个辅助函数:cons-all。要实现cons-all函数,需要用到内置的map过程。cons-all接收一个元素和一个list,将这个元素插入到list中的每个元素作为开头。

比如:

开发完成之后测试:

python3 ok -q 18答案

我们先来实现cons-all,这个函数逻辑并不复杂。

遍历rests中的每一个元素,然后将first元素拼接上去即可。题目提示了我们,可以使用内置的map。map这个过程会将某一个过程应用在一个list的所有元素上。

那么显然,我们只需要实现一个函数能够将first拼接在元素上,然后再调用map即可,也不需要递归了。

(define (cons-all first rests)(define (concat s)(cons first s))(map concat rests))

这道题我们之前在作业4当中用Python写过类似的,不知道大家还有没有印象。

不过当时求的是所有可能的数量,并且面额的*也稍有区别,不是固定的几种,而是给定了一个整数m,所有小于等于m的面值都有,当时的代码:

def count_partitions(n, m):"""Count the ways to partition n using parts up to m."""if n == 0:return 1elif n < 0:return 0elif m == 0:return 0else:return count_partitions(n-m, m) + count_partitions(n, m-1)

我们对它稍作改动,写成Python的版本:

def concat(v):return lambda x: [v] + xdef count_partitions(n, m):"""Count the ways to partition n using parts up to m."""if n == 0:return [[]]if m == 0 or n < 0:return [] # invalid resultif n >= m:ret = list(map(concat(m), count_partitions(n-m, m)))return ret + count_partitions(n, m-1)else:return count_partitions(n, m-1)

接下来要做的就是把上面这段Python代码转换成Lisp来实现,其实只要Python写得出来,Lisp也一样可以,语法虽然不同,但是核心原理是一样的。

这里由于Lisp递归的时候还涉及到参数的计算,写在一起会显得非常非常冗长。所以这里我们使用了define语句,简化了代码的书写。

(define (list-change total denoms); BEGIN PROBLEM 18(define (use-denom total denoms) (list-change (- total (car denoms)) denoms))(define (not-use-denom total denoms) (list-change total (cdr denoms)))(cond ((null? denoms) nil)((eq? total 0) (cons nil nil))((< total (car denoms)) (list-change total (cdr denoms)))(else (append (cons-all (car denoms) (use-denom total denoms)) (not-use-denom total denoms)))))Problem 19

在Scheme语言当中,源代码也是一种数据。每一个非原生表达式都可以被写成一个Scheme list。所以我们可以实现一个过程,它可以像是生成一个scheme list一样生成另外一段程序。

重写代码是非常有用的,比如我们可以只实现解释器的核心功能,然后将其他的功能通过转写的方式转化成解释器支持的核心部件来进行运行。这样可以简化解释器的开发,我不太清楚这是否是Lisp语言设计逻辑的一部分,但它的确惊艳到了我,这样的设计思路实在是太巧妙了。

比如let语句等价于lambda表达式,它们都会基于当前环境创建新的frame。你可以回顾一下Problem 15当中对于let语法的定义。

这两个表达式可以用下图来表示:

使用这个规则来实现let-to-lambda过程,它会将let特殊类型转化成lambda表达式。

如果我们quote一个let表达式,将它传入这个过程,那么我们会得到一个等价的lambda表达式,例如:

要想实现功能,let-to-lambda必须能够感知scheme语法。因为scheme表达式是递归嵌套的,所以let-to-lambda也必须是递归的。

实际上,let-to-lambda的结构和scheme_eval函数是相似的,不过是用scheme语言实现的。提示:单元素包括数字、bool、nil和符号。

提示:你需要先实现zip过程,map过程很有帮助

在编码之前,先答题确保题目理解正确

python3 ok -q 19 -u

编码之后,进行测试:

python3 ok -q 19答案

我们先来看zip过程,zip方法的输入是n x 2的二维list,我们返回的结果是一个2 x n的二维list。相当于对数据做了一个行列变换。

那么我们要做的就是将每一行中分别取出第一个和第二个元素来构成两个list,再把这两个list串在一起。

如果不使用循环,有种无从下手的感觉。好在题目中提示了我们,可以使用map。

代码如下:

(define (zip pairs)(list (map car pairs) (map cadr pairs)))

然后是let-to-lambda

这道题老师已经替我们搭好了框架,把所有要考虑的情况都替我们列好了,我们只需要依次实现每一种情况的处理方法即可,算是为我们降低了一些难度。

我们分别来看一下这几种情况:

(atom? expr)

表达式是一个atom,即数字、symbol、nil或者bool,已经是不能再evaluate的结果了,直接返回即可。

(quoted? expr)

表达式是一个quote语句,和本题没有关系,不需要做任何处理,直接返回。

(or (lambda? expr) (define? expr))

表达式是lambda或define语句,不能直接确定是否有关系。因为define和lambda语句都还可以进一步嵌套,嵌套的语句可能会包含let语句,所以我们要递归一下嵌套的部分。

老师使用let语句替我们提取出了form,params和body。form是类型,params是参数,body是主体。只有body当中可能嵌套,所以我们要递归调用body。

(let? expr)

我们要处理的核心关键,let语句有两个部分,一个是values一个是body。我们看一下let语句的语法,它是先定义一些symbol和值的映射,再定义symbol的计算方式。我们要做的是把映射当中的symbol和value分别提取出来作为lambda过程的formal和params,body作为lambda过程的body。

不过比较麻烦的是这三者中间都有可能存在嵌套,所以我们需要使用递归。

else

即其它情况,由于我们只判断了car expr,所以我们还需要继续递归调用,判断后面的内容。这里我直接使用了map过程

更多细节参考代码:

(define (enumerate s); BEGIN PROBLEM 17(define (enum-iter n s)(if (null? s) nil(cons(list n (car s))(enum-iter (+ n 1) (cdr s)))))(enum-iter 0 s))0

到这里,这三题就算是讲完了。

虽然只有三道题,但是完整做下来感觉收获还是非常大的。不仅对于Scheme这一门语言,而且对解释器这个项目也进一步加深了印象,可以说是收获满满,这一次的大作业的的确确非同凡响,因此非常非常推荐大家亲自试试。

原文:https://juejin.cn/post/7096374133377728520
日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(四)

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(四)大家好,日拱一卒,我是梁唐。本文始发于公众号Coder梁我们来继续肝伯克利CS61A的scheme project,这是本project的第四篇,如果漏掉了之前的内容,可以去翻一

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

日拱一卒,伯克利YYDS,用Python写一个Lisp解释器(五)大家好,日拱一卒,我是梁唐。本文始发于公众号Code梁我们继续来肝伯克利CS61A的scheme project,今天我们来聊最后一个部分,附加题部分。虽然附加题只有两道

日拱一卒,伯克利教你写SQL

那么你算是来对了,这一次的伯克利CS61A的作业就关于SQL的应用。 再次案例一下伯克利CS61A这门课程,它是我见过最好的计算机入门课程。囊括十分丰富的内容:Python、算法、数据结构、机器学习、数据可视化,甚至还用上了编译原理的知识,写了一个Lisp的解释器。 这次的作业关于SQL,需要我们用SQL根据题目要求筛选出指定的...

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
如果两个男生喜欢上同一个女生怎么办 玛丽居里学者 玛丽居里奖学金含金量 玛丽居里学者含金量 玛丽居里学者什么级别 豆渣肥料适合什么花 豆渣拌在土里能种花吗-豆渣怎样做肥料好 生物化学和微生物学是一个专业吗 请问这狗狗是什么品种?是不是杂交的啊? ...鉴别下这是啥狗狗?大概三个月不到点。个人感觉像是蝴蝶和土狗... 为什么y小于0时Fy(y)=0;又为什么y大于等于2时,Fy(y)=1 从一个单元格查找某个字符,返回文本,用IF函数只能循环7次,如何突破7... 利普刀手术能根治hpv吗 利普刀手术能治宫颈糜烂吗 什么是利普刀 ...转到新机去 换手机怎么将微信聊天记录转移到新手机里 利普刀治疗宫颈病变可以切干净吗 手机主卡可以看到副卡信息吗 常在沈阳逛街的回答一下吧 大连的想去沈阳逛街时间怎么安排啊? 求助沈阳朋友们几个小问题 win10水印怎么删除? 上厕所的时候大便有血怎么回事 手机用个人热点很费电吗 法律行政法规规定的其他条件证明法律行政法规规定的其他条件 怎么关闭手机淘宝的推送信息啊? 手机淘宝里面的消息怎么关闭啊? 首先发现电磁感应现象的科学家是 A.牛顿 B.爱因斯坦 C.法拉第 D.安 历史上第一个发现电磁感应现象的科学家是( )A.法拉第B.牛顿C.伽利略D... 梦见姥姥在超市买东西,买好的苹果梨水果忘在超市的柜子里,票也弄丢... ...但是还是很好动 怎么回事啊他只吃了一块就不再吃了. 我的小乌龟前两天还好好的,很好动很能吃,就这两天,不动了眼睛不睁了也... 乌龟不吃东西...怎么办? 怎么养好巴西红耳龟啊。。。昨晚回家突然看到大点那只死了。。。感觉... 我家的小乌龟不吃东西,都半个月了,它的眼睛弄好了,水温提高了,可是还 ... 我养的乌龟比鼠标大一点今天被我的小女儿踩了一下就不动了 好像快死... 查自己有什么保险怎么查? ...龟,其中有一只最近几天发现后腿有些瘸,不吃东西懒得动.放进饲料只... 您好,我养了两只小乌龟 是很小的那种,现在其中一只乌龟老是趴着脑袋... 总是在不忙或者特无聊的时候才想起一个人(男女朋友),这个要怎么... 这个乌龟多大了? 我有一个乌龟像死了一样。怎么也动不了了。另外一只冬眠刚醒。求救,它... 刚买的中华龟,大概有小半个拳头那么大(很小),是一对,现在不吃不喝好多... 乌龟为什么一动不动,眼是半睁状态,而且是春天, 是中华龟,很长时间没... 乌龟死了是不是有什么不好的说法? 求帮助 这是什么虫子 大神回答 网友们,求回答啊,这是什么虫子,家里有花是花里的吗?怎么去除啊?烦烦烦... 这是个什么虫子 求回答 我家的小龟现在天天撞鱼缸好像想出来、站起来扒着鱼缸,烦死了 ...这几天还老是伸长脖子张大嘴,发出声音,好像很难受的样子。