the little schemer 笔记(10)
第十章 What Is the Value of All of This?
entry条目 是由list表组成的 pair 对,pair 对的第一个list表是集合 set。另外,两个list表的长度必须是相同的。举出几个 entry条目 的例子。
例如((appetizer entree beverage) (pate boeuf vin)和((appetizer entree beverage) (beer beer beer))
还有((beverage dessert) ((food is) (number on with us)))
我们如何用一个集合的名字和一个列表的值构建一个 entry条目
(define new-entry build)
试试用这个函数来构建前边的 entry条目 例子。
(lookup-in-entry name entry)的值是多少,其中 name 是entree
taste
那假设 name 是 dessert 呢?
这种情况下, lookup-in-entry 什么也不做。
我们如何把这个完成?
当在 entry条目 中没有找到 name , lookup-in-entry 调用另一个参数
你认为这个额外的函数需要多少个参数?
我们认为需要一个,name。为什么?
这里是我们对 lookup-in-entry 的定义。
(define lookup-in-entry
(lambda (name entry entry-f)
(lookup-in-entry-help name
(first entry)
(second entry)
entry-f)))
补全函数 lookup-in-entry-help
(define lookup-in-entry-help
(lambda (name names values entry-f)
(cond
(_____ _____)
(_____ _____)
(_____ _____))))
(define lookup-in-entry-help
(lambda (name names values entry-f)
(cond
((null? names) (entry-f name))
((eq? (car names) name) (car values))
(else (lookup-in-entry-help name (cdr names) (cdr values) entry-f)))))
笔记:entry-f 就是那个额外的函数来专门处理(second entry)为空表的情况。
table表格(也称作 environment环境)是entry条目为成员的表。空表为()。举出其它的例子。
(((appetizer entree beverage) (pate boeuf vin))
((beverage dessert) ((food is) (number one with us))))
定义函数 extend-table, 该函数输入一个 entry 和一个 table 作为参数,然后把新的 entry 放在旧的 table 表格前建立一个新的 table 表格。
(define extend-table cons)
(lookup-in-table name table table-f)是什么,其中 name 是 entree,table 是
(((entree dessert)
(spaghetti spumoni))
((appetizer entree beverage)
(food tastes good)))
table-f 是(lambda (name) ...)
可能是 spaghetti 或者 tastes, 但是 lookup-in-table 是按照表中的 entry 的顺序来查找的。所以是 spaghetti。
写出函数 lookup-in-table
提示:别忘了用一些帮助。
(define lookup-in-table
(lambda (name table table-f)
(cond
((null? table) (table-f name))
(else (lookup-in-entry name
(car table)
(lambda (name)
(lookup-in-table name (cdr table) table-f)))))))
你能描述一下,下面这个函数的作用吗?
(lambda (name)
(lookup-in-table name (cdr table) table-f))
当 name 在第一个 entry 中没有找到时的执行动作。
前面的preface中我们提到 sans serif 字体表示原子。这一点并不要紧。不用注意原子是否是 sans serif 字体了。
我们为 expression表达式选择了好的表示吗?
是的。它们都是 S-expression,所以它们都可以作为函数的数据。
什么样的函数?
比如 value。
还记得第六章的 value 吗?
对表达式调用 value 返回的是它的自然值。
(car (quote (a b c))) 的值是多少?
不知道。
(cons rep-a
(cons rep-b
(cons rep-c
(quote ()))))
的值是多少?其中 rep-a 是a,rep-b 是b,rep-c 是c
(a b c)
非常棒。那这个值呢?
(cons rep-car
(cons (cons rep-quote
(cons
(cons rep-a
(cons rep-b
(cons rep-c
(quote ()))))
(quote ())))
(quote ())))
其中
rep-car 是 car
rep-quote 是 quote
rep-a 是 a,rep-b 是b,rep-c 是c
它是表达式(car (quote (a b c)))的表示。
(car (quote (a b c))) 的值是多少?
a
(value e)的值是多少?其中e是(car (quote (a b c)))
a
(value e)的值是多少?其中e是(quote (car (quote (a b c))))
(car (quote (a b c)))
(value e)的值是多少?其中e是(add1 6)
7
(value e)的值是多少?其中e是(quote nothing)
nothing
(value e)的值是多少?其中e是 nothing
nothing没有value
(value e) 是什么,其中e是
((lambda (nothing)
(cons nothing (quote ())))
(quote (from nothing comes something)))
((from nothing comes something))
(value e) 是什么,其中e是
((lambda (nothing)
(cond
(nothing (quote something))
(else (quote nothing))))
#t)
something。
e的类型是什么,其中e是6
*const
e的类型是什么,其中e是#f
*const
(value e)的值是什么,其中e是#f
#f
e的类型是什么,其中e是cons
*const
(value e) 的值是什么,其中e是car
(primitive car)
e的类型是什么,其中e是(quote nothing)
*quote
e的类型是什么,其中e是nothing
*dentifier
e的类型是什么,其中e是(lambda (x y) (cons x y))
*lambda
e的类型是什么,其中e是
((lambda (nothing)
(cond
(nothing (quote something))
(else (quote nothing))))
#t)
*application
e的类型是什么,其中e是
(cond
(nothing (quote something))
(else (quote nothing)))
*cond
你认为有多少种类型?
我们找到六种:
*const
*quote
*identifier
*lambda
*cond
*application
你认为我们如何表示类型?
我们选择函数,它被称为“action”执行动作。
如果action都是那些应用到特定类型的表达式时会做该做的事情的函数,那么 value 应该是什么?
你猜到了。它必须找出传递给它的类型然后使用相应的执行动作。
记得第八章的 atom-to-function 吗?
当我们重写 value 时,我们需要 atom-to-function。
下面这个函数能够对每一个 S-expression 执行正确的过程。
(define expression-to-action
(lambda (e)
(cond
((atom? e) (atom-to-action e))
(else (list-to-action e)))))
写出函数 atom-to-action。
注:这里不考虑病态的(ill-formed) S-expression 表达式,例如(quote a b), (), (lambda (#t) #t), (lambda (5) 5), (lambda (car) car), (lambda a a), (cond (3 c) (else b) (6 a)), (1 2)等。它们可以在传递给value之前用一个合适的函数来侦测。
(define atom-to-action
(lambda (e)
(cond
((number? e) *const)
((eq? e #t) *const)
((eq? e (quote cons)) *const)
((eq? e (quote car)) *const)
((eq? e (quote cdr)) *const)
((eq? e (quote null?)) *const)
((eq? e (quote eq?)) *const)
((eq? e (quote atom?)) *const)
((eq? e (quote zero?)) *const)
((eq? e (quote add1)) *const)
((eq? e (quote sub1)) *const)
((eq? e (quote number?)) *const)
(else *identifier))))
现在定义函数 list-to-action
(define list-to-action
(lambda (e)
(cond
((atom? (car e))
(cond
((eq? (car e) (quote quote)) *quote)
((eq? (car e) (quote lambda)) *lambda)
((eq? (car e) (quote cond)) *cond)
(else *application)))
(else *application))))
假设 expression-to-action 执行正常,我们就可以定义 value 和 meaning
(define value
(lambda (e)
(meaning e (quote ()))))
(define meaning
(lambda (e table)
((expression-to-action e) e table)))
value 中的(quote ())是什么?
它是空表格。函数 value 以及它用的函数叫做解释器。
注:数 value 函数是个 Scheme(或者Lisp)中eval的近似。
Actions do speak louder than words.
action需要多少个参数?
两个,表达式 e和一个 table 表格
下面这个是constant的action执行动作。
(define *const
(lambda (e table)
(cond
((number? e) e)
((eq? e #t) #t)
((eq? e #f) #f)
(else (build (quote primitive) e)))))
对吗?
对,对于数,它直接返回表达式,对于#t,它返回true,对于#f它返回false
其它的constant类型的原子则代表 primitive。
下面是 *quote 的 action 执行动作
(define *quote
(lambda (e table)
(text-of e)))
定义辅助函数 text-of
(define text-of second)
我们用过table吗?
还没呢,不过一会就用到 table 了。
我们为甚么要用到 table?
存储 identifier 的 value。
假设 table 存储着 identifier 的 value,写出 *identifier 的执行动作。
(define *identifier
(lambda (e table)
(lookup-in-table e table initial-table)))
下面是 initial-table
(define initial-table
(lambda (name)
(car (quote ()))))
什么时候用它?
我们希望永远都不要用到。为什么?
笔记:lookup-in-table 的第三个参数是用来处理某个name对应是空表的情况,这时table是有问题的,table本来是给出identifier标识符的对应值的。没有给出identifier的值,于是直接抛出一个错误了,(car (quote))是违反primitive 的car操作定义的。
(lambda (x) x) 这样的表达式的 value 是什么?
我们不知道,但是我们知道它是一个 non-primitive 函数。
non-primitive 函数与 primitive有什么不同?
我们知道 primitive 做了什么; non-primitive 是由它们的参数和函数体构成。
所以当我们用一个 non-primitive, 我们需要存储它的参数和函数体。
至少,所幸的是,这只是一个 lambda 表达式的 cdr。
我们还需要存储什么?
我们还需要把 table 表格存储了,方便后边使用。
我们如何表示它?
当然是存储在 list 表中。
下面是 *lambda 的 action 执行动作。
(define *lambda
(lambda (e table)
(build (quote non-primitive)
(cons table (cdr e)))))
(meaning e table)是什么,其中e是(lambda (x) (cons x y)),table 是(((y z) ((8) 9)))。
(non-primitive ( (((y z) ((8) 9))) (x) (cons x y) ))
~~~~~~~~~~~ ```` ^^^^^^
table formals body
定义辅助函数来提取三个元素的list(即,table,formals,body)。写出 table-of formals-of 和 body-of
(define table-of first)
(define formals-of second)
(define body-of third)
请您用自己的话描述(cond ...)
cond行是特殊的格式。它一行行往下查找。如果问题部分为假,它往下查找。否则运行为真的部分。如果遇到了 else行,cond认为它为真,直接执行else部分。
下面这个 evcon 就是做我们刚才说的那些事情。
(define evcon
(lambda (lines table)
(cond
((else? (question-of (car lines)))
(meaning (answer-of (car lines)) table))
((meaning (question-of (car lines)) table)
(meaning (answer-of (car lines)) table))
(else (evcon (cdr lines) table)))))
(define else?
(lambda (x)
(cond
((atom? x) (eq? x (quote else)))
(else #f))))
(define question-of first)
(define answer-of second)
我们违反了第一戒律了吗?
是的,我们么有查询(null? lines), 所以 cond 中的问题得至少有一个为真。
现在用函数 evcon 来写一个 *cond 执行动作。
(define *cond
(lambda (e table)
(evcon (cond-lines-of e) table)))
(define cond-lines-of cdr)
辅助函数有用吗?
有用,它们使可读性提高了。你已经知道了。
你现在理解了 *cond 吗?
可能还没有。
你如何熟悉*cond?
最好的方式是来个例子。一个好例子:(*cond e table), 其中e是(cond (coffee klastsch) (else party)), table 是 (((coffee) (#t)) ((klastsch party) (5 (6))))
见过 table 是怎么用的吗?
见过啊, *lambda 和 *identifier 都用过。
但是 identifier 如何查找 table?
使用为一个我们还没有定义的 action: *application。
application 是如何表示的?
一个 application 就是一个 list,它的 car 是一个值是函数的 expression 表达式。
application 与(and ...) (or ...) (cond...) 等有什么不同之处。
一个 application 必须确定它的所有参数。
在我们执行一个函数之前,我们必须知道所有参数吗?
必须。
写一个函数 evlis ,它的参数是由一个用来表示参数的list和一个table构成,返回每一个参数的含义。
(define evlis
(lambda (args table)
(cond
((null? args) (quote ()))
(else
(cons (meaning (car args) table)
(evlis (cdr args) table))))))
在确定 application 的含义之前我们还需要什么?
我们需要知道它的 function-of 的含义。
下面是 *application
(define *application
(lambda (e table)
(apply
(meaning (function-of e) table)
(evlis (arguments-of e) table))))
对吗?
当然对了。我们只需要正确定义 apply, function-of, arguments-of。
写出 function-of 和 arguments-of
(define function-of car)
(define arguments-of cdr)
有多少个类型的函数?
两种:primitive 和 non-primitive。
这两种函数表示什么?
(primitive primitive-name) 和 (non-primitive (table formals body)), 表(table formals body)称为 closure record。
写出 primitive? 和 non-primitive?
(define primitive?
(lambda (l)
(eq? (first l) (quote primitive))))
(define non-primitive?
(lambda (l)
(eq? (first l) (quote non-primitive))))
现在我们可以写出函数 apply 了。
(define apply
(lambda (fun vals)
(cond
((primitive? fun)
(apply-primitive (second fun) vals))
((non-primitive? fun) (apply-closure (second fun) vals)))))
注:如果 fun 既不计算 primitive 也不计算 non-primitive, 例如表达式((lambda (x) (x 5)) 3), 那就没有答案。函数 apply 是Scheme (Lisp) 中apply的近似。
填空
(define apply-primitive
(lambda (name vals)
(cond
((eq? name _____)
(cons (first vals) (second vals)))
((eq? name (quote car))
(car (first vals)))
((eq? name (quote vals))
(_____ (first vals)))
((eq? name (quote null?))
(null? (first vals)))
((eq? name (quote eq?))
(_____ (first _____)))
((eq? name (quote atom?))
(_____ (first vals)))
((eq? name (quote zero?))
(zero? (first vals)))
((eq? name (quote add1))
(add1 (first vals)))
((eq? name (quote sub1))
(sub1 (first vals)))
((eq? name (quote number?))
(number? (first vals))))))
1.(quote cons)
2.cdr
3.eq?
4.(second vals)
5.:atom?
(define :atom?
(lambda (x)
(cond
((atom? x) #t)
((null? x) #t)
((eq? (car x) (quote primitive)) #t)
(else #f))))
注:函数 apply-primitive 能够查找 application 的 cdr 。The function apply·primitive could check for applications of cdr to the empty list or sub1 to 0, etc.
apply-closure 是唯一没有定义的函数了?
只剩它了, apply-closure 必须展开 table。
我们如何得到(f a b)的值,其中f是(lambda (x y) (cons x y)), a是1,b是(2)
这个很是麻烦。不过我们知道怎样做才能得到(cons x y)的含义。
我们为什么做这个?
这个不需要 apply-closure。
你能概括最后两个步骤吗?
对一个 value 的list表应用一个 non-primitive(一个closure)同下面这个做法是一致的:对closure的body和它的展开的table查找结果。其中table展开条目entry格式为(formals values)。 在条目 entry 中,formals 是 closure 的formals,values是 evlis的的结果。
所有这些你都遵守了吗?
如果没有,下面是 apply-closure 的定义。
(define apply-closure
(lambda (closure vals)
(meaning (body-of closure)
(extend-table
(new-entry (formals-of closure) vals) (table-of closure)))))
这是一个复杂的函数,得要一个例子。
以下内容假设:
closure是((((u v w) (1 2 3)) ((x y z) (4 5 6))) (x y) (cons z x)), vals 是((a b c) (d e f))
meaning 的新参数是什么?
meaning 新的参数e将是(cons z x),table将是
(((x y) ((a b c) (d e f))) ((u v w) (1 2 3) (4 5 6)))
(cons z x)的含义是什么,其中z是6,x是(a b c)
就是(meaning e table), 其中e是(cons z x),table是
(((x y) ((a b c) (d e f))) ((u v w) (1 2 3)) ((x y z) (4 5 6)))
让我们找出所有的参数的含义, (evlis args table), 其中args是
(((x y) ((a b c) (d e f))) ((u v w) (1 2 3)) ((x y z) (4 5 6)))
我们必须先确定(meaning e table), 其中e是z;(meaning e table)其中e是x。
(meaning e table)是什么,其中e是z
6, 用的 *identifier。
(meaning e table)是什么,其中e是x
(a b c), 用的 *identifier。
所以 evlis 的结果是什么?
(6 (a b c)), 因为evlis返回一个list的含义。
(meaning e table)是什么,e是cons
(primitive cons), 用的 *const。
我们现在可以 (apply fun vals)了,其中fun是 (primitive cons), vals是(6 (a b c)), 我们应该用哪个path路径?
apply-primitive path路径。
(apply-primitive name vals) 选择cond的哪个分支?其中name是cons,vals是(6 (a b c))
第三个:
((eq? name (quote cons))
(cons (first vals) (second vals)))。
我们完成了吗?
完成了。
但是(define ...)呢?
没有用到define,因为递归可以用Y combinator算子获得。
不需要定义(define ...)吗?
需要,不过需要看本书的后续 Seasoned Schemer。
意思就是如果我们用Y combinator 转换一下,就可以在解释器上运行解释器?
是的,but don't bother。
value有什么不同之处?
它查找 expression 表达式的表示。
will-stop?能查找 expression 的表示吗?
这个很有帮助。
有用吗?
No, don't bother——we can play the same game again. 我们可以定义一个像 last-try? 函数那样可以展现不能定义will-stop?函数的函数。
else
好了,该是宴会的时候了。
你已经到达了中场休息。你的选择呢?你可以马上接下册 The Seasoned Schemer, 或者你可以都一些我们下面提到的书。所有这些书都是经典,有一些比较老;然而它们都能经受的主时间的检验,值得你们去选择。有一些与数学,逻辑等等都无关,有些必须有数学基础,但仅仅是讲些有趣的故事,另外一些值得去发现。无需怀疑:这些书不是用来当作续集来读,就是给你娱乐休闲的。在 The Seasoned Schemer 的结尾你尅有找着一些 Scheme 和 Common Lisp 的书。不要感到到必须读续集,而是该花点时间下面的这些书。当你放松点了,消化了强加给你的卡路里,你再继续读下册。Enjoy!
Abbott, Edwin A. Flatland . Dover Publications, Inc., New York, 1952. (Original publication: Seeley and Co. , Ltd . , London, 1884.)
Carroll, Lewis. The Annotated Alice: Alice 's Adventures in Wonderland and Through the Looking Glass. Clarkson M. Potter, Inc., New York, 1960. Introduction and notes by Martin Gardner. Original publications under different titles:
, Macmillan and
Company, London 1865 and 1872, respectively.
Halmos, Paul R. Naive Set Theory. Litton Educational Publishers, New York, 1960.
Hein, Piet. Grooks . The MIT Press, Cambridge, Massachusetts, 1960.
Hofstadter, Douglas R. Godel, Escher, Bach: an Eternal Golden Bmid. Basic Books, Inc . , New York, 1979.
Nagel, Ernest and James R. Newman . Godel 's Proof. New York University Press, New York, 1958.
P6lya, Gyorgy. How to Solve It. Doubleday and Co. , New York, 1957.
Smullyan, Raymond. To Mock a Mockingbird And Other Logic Puzzles Including anAmazing Adventure in Combinatory Logic. Alfred A. Knopf, Inc., New York, 1985.
Suppes, Patrick. Introduction to Logic. Van Nostrand Co. , Princeton , New Jersey, 1957.
the little schemer 笔记(10)的更多相关文章
- the little schemer 笔记(10.1)
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 China Mainla ...
- 操作系统概念学习笔记 10 CPU调度
操作系统概念学习笔记 10 CPU调度 多道程序操作系统的基础.通过在进程之间切换CPU.操作系统能够提高计算机的吞吐率. 对于单处理器系统.每次仅仅同意一个进程执行:不论什么其它进程必须等待,直到C ...
- thinkphp学习笔记10—看不懂的路由规则
原文:thinkphp学习笔记10-看不懂的路由规则 路由这部分貌似在实际工作中没有怎么设计过,只是在用默认的设置,在手册里面看到部分,艰涩难懂. 1.路由定义 要使用路由功能需要支持PATH_INF ...
- 《C++ Primer Plus》学习笔记10
<C++ Primer Plus>学习笔记10 <<<<<<<<<<<<<<<<<&l ...
- SQL反模式学习笔记10 取整错误
目标:使用小数取代整数 反模式:使用Float类型 根据IEEE754标识,float类型使用二进制格式编码实数数据. 缺点:(1)舍入的必要性: 并不是所有的十进制中描述的信息都能使用二进制存储,处 ...
- JAVA自学笔记10
JAVA自学笔记10 1.形式参数与返回值 1)类名作为形式参数(基本类型.引用类型) 作形参必须是类的对象 2)抽象类名作形参 需要该抽象类的子类对象,通过多态实现 3)接口名为形参 需要的是该接口 ...
- golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息
golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放 ...
- Spring MVC 学习笔记10 —— 实现简单的用户管理(4.3)用户登录显示全局异常信息
</pre>Spring MVC 学习笔记10 -- 实现简单的用户管理(4.3)用户登录--显示全局异常信息<p></p><p></p>& ...
- Python标准库笔记(10) — itertools模块
itertools 用于更高效地创建迭代器的函数工具. itertools 提供的功能受Clojure,Haskell,APL和SML等函数式编程语言的类似功能的启发.它们的目的是快速有效地使用内存, ...
随机推荐
- ASP.NET MVC判断基于Cookie的Session过期
当我们第一次请求访问时,可以看到Response的Set-Cookie里添加了ASP.NET_SessionId的值,以后再访问时可以看到Resquest里的Cookie已经包含这个Key. Se ...
- 回溯法——求解N皇后问题
问题描写叙述 八皇后问题是十九世纪著名数学家高斯于1850年提出的.问题是:在8*8的棋盘上摆放8个皇后.使其不能互相攻击,即随意的两个皇后不能处在允许行.同一列,或允许斜线上. 能够把八皇后问题拓展 ...
- 【POJ 1159】Palindrome
[POJ 1159]Palindrome 近期各种题各种奇葩思路已经司空见惯了...又新出个滚动数组= = 该题另一点须要知道 最少须要补充的字母数 = 原序列S的长度 - S和S'的最长公共子串长度 ...
- 解决virtualbox安装增强工具失败的问题
virtualbox有个增强工具,安装之后用户体验是非常爽的.但是有些时候在安装增强工具会遇到一些小问题,无非是没有安装gcc,make之类的编译工具或是需要安装kernel*.而我遇到的问题在做了这 ...
- codeforces 447C. DZY Loves Sequences 解题报告(446A)
题目链接:http://codeforces.com/problemset/problem/447/C 题目意思:给出 一个 包含 n 个数的序列你,从中需要找出这个序列的最长子串,满足在里面只修改其 ...
- html5--6-60 阶段练习7-下拉菜单
html5--6-60 阶段练习7-下拉菜单 学习要点 综合运用所学过的知识完成一个下拉菜单的小练习,加深对学过知识点的综合应用能力. <!DOCTYPE html> <html l ...
- 基于C#实现Windows服务状态启动和停止服务的方法
网址:http://www.jb51.net/article/89230.htm
- macbook pro上安装虚拟机
第一步:下载MacHunter的app应用商店 第二步:在MacHunter内下载Parallels Desktop虚拟机 第三步:如果在这个商店下载不下来,在网络资源上直接下载Parallels D ...
- 分享Ubuntu下一些很棒的软件(一)
分享一些我在Ubuntu下常用的软件. Goolge Chrome/Firefox/Thunderbird这些重量级的跨平台的软件虽然很强大,但大家应该都比较熟悉了,没有太多必要在这里介绍.本文涉及到 ...
- 《算法概论》第八章的一些课后题目 关于NP-Complete Problem
8.3 STINGY SAT STINGY SAT is the following problem: given a set of clauses (each a disjunction of li ...