第十章 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)的更多相关文章

  1. the little schemer 笔记(10.1)

    This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 China Mainla ...

  2. 操作系统概念学习笔记 10 CPU调度

    操作系统概念学习笔记 10 CPU调度 多道程序操作系统的基础.通过在进程之间切换CPU.操作系统能够提高计算机的吞吐率. 对于单处理器系统.每次仅仅同意一个进程执行:不论什么其它进程必须等待,直到C ...

  3. thinkphp学习笔记10—看不懂的路由规则

    原文:thinkphp学习笔记10-看不懂的路由规则 路由这部分貌似在实际工作中没有怎么设计过,只是在用默认的设置,在手册里面看到部分,艰涩难懂. 1.路由定义 要使用路由功能需要支持PATH_INF ...

  4. 《C++ Primer Plus》学习笔记10

    <C++ Primer Plus>学习笔记10 <<<<<<<<<<<<<<<<<&l ...

  5. SQL反模式学习笔记10 取整错误

    目标:使用小数取代整数 反模式:使用Float类型 根据IEEE754标识,float类型使用二进制格式编码实数数据. 缺点:(1)舍入的必要性: 并不是所有的十进制中描述的信息都能使用二进制存储,处 ...

  6. JAVA自学笔记10

    JAVA自学笔记10 1.形式参数与返回值 1)类名作为形式参数(基本类型.引用类型) 作形参必须是类的对象 2)抽象类名作形参 需要该抽象类的子类对象,通过多态实现 3)接口名为形参 需要的是该接口 ...

  7. golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息

    golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放 ...

  8. Spring MVC 学习笔记10 —— 实现简单的用户管理(4.3)用户登录显示全局异常信息

    </pre>Spring MVC 学习笔记10 -- 实现简单的用户管理(4.3)用户登录--显示全局异常信息<p></p><p></p>& ...

  9. Python标准库笔记(10) — itertools模块

    itertools 用于更高效地创建迭代器的函数工具. itertools 提供的功能受Clojure,Haskell,APL和SML等函数式编程语言的类似功能的启发.它们的目的是快速有效地使用内存, ...

随机推荐

  1. 一套扁平化界面风格的flex 皮肤

    意外在git上发现这个项目.似乎刚開始不久.部分控件的新皮肤似乎还没完毕.只是个人感觉挺不错的.大家认为呢? =>git地址:https://github.com/akamud/FlatSpar ...

  2. layer弹出层不居中解决方案,仅显示遮罩,没有弹窗

    问题:项目中layer询问层的弹窗仅显示遮罩层,并不显示弹窗…… 原因:图片太多将layer弹窗挤出屏幕下方,看不见了…… 解决方案:让layer的弹出层居中显示 一.问题描述 用layer做操作结果 ...

  3. Asp.net常用的51个代码(非常实用)

    1.//弹出对话框.点击转向指定页面 Response.Write("<script>window.alert('该会员没有提交申请,请重新提交!')</script> ...

  4. NullpointerException真的一定要被预防?

    毫无疑问,空指针NullpointerException是我们最常遇到异常,没有之一! 在刚进入编程职业时,我想,大部分进入的同学肯定会受到前辈们的叮咛:一定要防止空指针,这是个低级错误.你们不是?好 ...

  5. sanic官方文档解析之Exception和Middleware,Listeners

    1,异常 异常是从处理请求内部抛出来的,并且通过Sanic自动的被处理异常,,异常用第一个参数携带异常信息,还可以接受在HTTP响应中要传递回的状态代码.引发异常 1.1引发异常 自动触发异常,,简单 ...

  6. golang---信号signal

    golang中os/signal包的使用 chenbaoke · 2015-06-17 20:03:59 · 2748 次点击 · 预计阅读时间 1 分钟 · 不到1分钟之前 开始浏览 这是一个创建于 ...

  7. [RK3288]PMU配置(RK808)【转】

    本文转载自:http://www.javashuo.com/content/p-6398007.html 硬件原理 pmic 电路原理 平台概述 RK808 PWM 介绍 驱动分析 dts 驱动流程 ...

  8. Linux 进程等待队列【转】

    本文转载自:http://blog.csdn.net/dlutbrucezhang/article/details/9212067 Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制 ...

  9. Spring Boot2.0+Redis+Ehcache实现二级缓存

    EHCache 本地缓存 Redis 分布式缓存(可以共享) 一级 Redis 二级Ehcache    当redis挂了 有备胎 反之: 先走本地,本地没有再走网络  尽量少走Redis  效率会高 ...

  10. Silverlight中使用MVVM(2)

    Silverlight中使用MVVM(1)--基础 Silverlight中使用MVVM(2)—提高 Silverlight中使用MVVM(3)—进阶 Silverlight中使用MVVM(4)—演练 ...