SICP 第一章 构造过程抽象 1

Chapter 1 Building Abstractions with Procedures

Chapter 1 Building Abstractions with Procedures

1 构造过程抽象

  1. 心智活动就是:
    1. 组合. 将 simple ideas 变成 compound ideas
    2. 关系. 将idea放在一起, setting them by one another, 同时观察.
    3. 抽象. 隔离开实际中的相伴的其他idea, 从而抽取出 general idea.
  2. 我们研究 the idea of a computational process.
    1. 计算过程(computational process) 计算机中的 抽闲存在, 操作 data.
    2. 一套规则即 program 指导process的evolution. 就像法师用咒语控制精灵. 用咒语(program)来施展法术(process). procedure是咒语实体
  3. master有organize programs 的能力:预见,合理规划;防灾救灾;模块化.
  4. Lisp 是一种语言 describing processes. 类似 自然语言用来描述everyday thougths, 数学用来描述定量现象.
  5. Lisp本意只是一种数学记述形式, 用来处理一种特定形式的逻辑表达式(递归方程); 但也适合作为program.
  6. 名字来自表处理list processing. 通过新增原子和表, 获得符号计算的能力,如微积分;
  7. 最重要的一个特征是: process的Lisp描述(称为procedure)本身又可以做为Lisp的数据来表示和操作 许多威力强大的程序设计法术都是为了: 填平"被动的"数据 和 "主动的"过程 之间的传统划分 represent prcedures as data 的能力 使得 Lisp 非常适合于 写programs that must 操作其他 programs as data, 例如: interpreters, compilers.

1.1 程序设计的基本元素

  • 程序设计语言不仅指挥计算机,更是一种框架,让人在其中组织思想
  • 都有将简单认识组织成更复杂认识的方法,3种机制: 完全对应心智活动
    • 基本表达式,最简单的个体
    • 组合的方法,从简单东西出发构造出复合的元素
    • 抽象的方法,给复合对象命名,当作单元去操作
  • 程序设计中,处理两类要素:过程和数据 并不严格分离 ;简单说,数据是操作的"东西",而过程是操作数据的规则的描述. 对数据和过程都要提供上述3种机制

1.1.1 表达式(expressions)

  • 基本表达式:486和+都是.
  • 组合表达式(combinations):一对括号括起的一个表,表示 过程应用. 最左运算符,其余是运算对象 求值就是将运算符表示的过程 用于运算对象的值(实际参数)
  • 前缀表示, 优点:
    1. 使用可带任意个实参的过程,而没有歧义
    2. 易嵌套,允许组合式元素本身又是组合式

1.1.2 命名和环境

  • using names to refer to computational objects. We say that the name identifies a variable whose value is the object.
  • (define var 2) define是最简单的抽象方法,允许用简单名字去引用 组合运算的结果 emacs lisp: (set '<name> <value>) 或者 (setq <name> <value>)
  • 实际上, 构造程序,就是逐步创建出越来越复杂的计算性对象 ;逐步创建名字-对象关联, 解释器提供这种便利,并促进采用递增方式去开发调试. 这里所说的计算对象是维护在解释其中的环境?似乎可以拆分为, 构建和计算?
  • 环境 将值与符号关联,解释器必须维护名字-值对偶, 确定符号的意义;这种存储就称为环境;一个计算过程中可能有多个不同环境

1.1.3 组合式的求值

  • 组合式求值规则:
    1. 求值各子表达式;
    2. 将最左子式的值,应用于所有其他子式的值
  • 递归,树形积累
    1. 这一规则,是递归的,有调用规则本身的需要 子式又是组合式
    2. 可用一棵树来表示求值过程,结点为组合式,发出分支对应子式;求值就是从端点向上穿行组合,进行"树形积累" 每个节点都把孩子收到一起
    3. 反复用步骤一,直到不再遇到组合式而是基本表达式 不是函数调用,或者是使用内部运算符.即, 找到了机器码.,取出数和名字的值
      • 基本表达式:
        1. 数的值, 就是它所表示的 数值
        2. 内部运算符的值, 就是相应的 机器指令序列
        3. 其他名字的值 在环境中查找
  • 特殊形式
    1. 如define, 不是组合式 ,有自己的求值规则.
  • 这些不同的表达式(expression)(及相应求值规则)组成语法形式. the evaluation rule for expressions 可表述为 普通形式 + 特殊形式.

1.1.4 复合过程(compound procedures)

  • 过程定义的一般形式:(define (<name> <formal parameters>) <body>) 复合过程对应基本过程 emcas lisp: (defun name (formal parameters) <body>)
  • 过程定义,强大的抽象技术,为复合操作提供名字,以作为单元使用,隐藏实现, 然后使用就与基本过程完全一样。

1.1.5 过程应用的代换模型

  • 基本过程应用机制:解释器已做好 复合过程用用机制:形参用实参取代,对过程体求值.
  • 过程应用的代换模型: 取出函数体, 应用参数, 求解; 归于为其他问题, 不断求解.
    • 只是帮助理解意义, 不真操作过程正文,去代换; 实际是利用局部环境,产生"代换"的效果。
    • 第一个且简化的模型, 当问题变复杂时, 会引入其他模型.
  1. 应用序和正则序

    正则序求值(normal-order evaluation): fully expand and then reduce 应用需求值(applicative-order evaluation): evaluate the arguments and then apply

    • 代换模型可用时, 两者结果一样.
    • 但, 正则序可能有重复求值 不同位置的形参都要计算一次; 代换模型不可用时, 正则序会很复杂.

1.1.6 条件表达式和谓词

  • cond,if
    • (cond (<p1> <e1>) (<p2> <e2>)…), 依次 看谓词pi,直到某谓词为真
    • else特殊符号,用在cond最后一句,永远返回,即else后谓词永为真 emacs lisp: 没有else, 用t代替.
    • if是cond只有两情况的特例, 算predicate, 然后 选择一个分支;形式(if <predicate> <consequent> <alternative>)
  • 逻辑符合运算符
    • and,or,not

1.1.7 练习

					;1.1
10
(+ 5 3 4)
(- 9 1)
(/ 6 2)
(+ (* 2 4) (- 4 6))
(set 'a 3)
a
(set 'b (+ a 1)) ;如果不quote, 就会求值. set的两个参数都可以quote
(setq a 3 b (+ a 1)) ;第一个参数自动quote; 多对同时赋值, 两个一对.
(+ a b (* a b))
(= a b)
(if (and (> b a) (< b (* a b)))
    b
  a)
(cond ((= a 4) 6)
      ((= b 4) (+ 6 7 a))
      (t 25 ))
;emacs lisp的cond里面没有else, 用t. t的value是t.
(+ 2 (if (> b a) b a))
(* (cond ((> a b) a)
	 ((> a b) b)
	 (t -1))
   (+ a 1))


					;1.2
(/ (+ 5
      5
      (- 2
	 (- 3
	    (+ 6
	       (/ 4 5))))
      (* 3 (- 6 2) (- 2 7)))


					;1.3
(defun bigest (a b c)
    (cond ((and (> a b)(> a c)) a)
	  ((and (> b a)(> b c)) b)
	  (t c)
	  )
    )
;function 的定义形式和 scheme不一样.
(bigest 1 2 3)
(bigest 21 89 30)

					;1.5
(defun p ()
  (p))
(defun test (x y)
  (if (= x 0)
      0
    y))
(test 0 (p));应用序的话, 会不断求(p), 死循环; 正则序不会求(p), 直接0结束.

1.1.8 实例:采用牛顿法求平方根

  • 说明与行动,数学函数与计算过程 有趣的观点. 贯穿在现实生活中. 说的正确性, 做的现实性. 平方根函数定义:根号x = y,使得y>=0且y^2=x 正统的数学函数,能推导出一些一般性事实;但并没有描述计算过程,给一个x,如何找到y 数学函数与计算过程的差异是说明性知识 与 行动性知识 间的普遍性差异的一个具体体现 数学关心说明性的描述(是什么),而CS关心行动性的描述(怎么做)
  • 求平方根的牛顿法: 已有猜测y, 新猜测为 y 与 x/y 的平均值,该值会更接近实际的平方根
  • 不用特殊迭代结构来实现迭代

1.1.9 过程作为黑箱抽象

  • 过程抽象
    • sqrt有多个有明确分工的过程构成. 直接反应了从原问题到子问题的分解.
    • sqrt中square是一过程抽象;任何能计算出平方的过程都可用 一个过程定义需要能隐藏起一些细节,能被当作黑箱使用,不用弄清其中的具体实现。
  • 局部名
    • 约束变量: 形式参数的名字, 名字具体是什么不重要, 可统一更换, 约束于的一集表达式称为 该名字的 作用域
    • 自由变量: 非约束变量, 不能任意替换.
  • 内部定义和块结构
    • 对用户由暴露必要的过程,其他都定义到该过程内部; 这种嵌套定义,称为 块结构 ,解决名字包装(减少冲突)问题
    • 部分参数传递就不必要了,可以改为内部定义中的 约束变量, 这种方式称 词法作用域 , 省去很多参数传递. 约束变量在某范围内是有定义的. 自由超越了一定的范围? 约束,自由都是对于一定范围来说的吧?

1.1.10 练习

					;1.7
(defun sqrt-iter (guess x)
  (if (good-enough? guess x)
      guess
    (sqrt-iter (improve guess x)
	       x)))
(defun improve (guess x)
  (average guess (/ x guess)))
(defun average (x y)
  (/ (+ x y) 2))
(defun good-enough? (guess x)
  (< (abs (- (square guess) x)) 0.001))
(defun square (x)
  (* x x))
(defun sqrt (x)
  (sqrt-iter 1.0 x))
(defun good-enough? (guess x);用变动比例来衡量结束时间
  (< (/ (abs (- (square guess) x)) x) 0.001));guess是开方结果, 而x是被开方数.

(sqrt 9)
(sqrt 100000)
(square (sqrt 2984881264950492));统计一下计算次数呢?
(sqrt 0.09)
(square (sqrt 0.0009)) ;小值, 误差很大. 因为结束条件与0.001相关.

					;1.8
(defun cube-root (x)
  (sqrt-iter 1.0 x)) ;sqrt-iter不只用来求sqrt, 而是一种更通用的形式
(defun improve (guess x)
  (/ (+ (/ x (square guess)) (* 2 guess))3))
(defun cube (x)
  (* x x x))
(defun good-enough? (guess x)
  (< (abs (- (cube guess) x)) 0.00001)) ;cube, square写到了里面. good-enough?就不得不写两份.

(cube (cube-root 9))
(cube (cube-root 0.0003))
  • ex1_6: if,cond都是特殊形式,与一般求值规则不同. 都是"从上到下"计算. 定义的new-if使用组合式的一般求值规则, 没有短路性,所有子表达式都会计算,而死循环。
  • ex1_7:对很大和很小的y,good-enough?不用固定差值了,而用相对值,用变化和猜测值的比率. 太小直接停了. 太大结束不了, 左右震荡.

是什么和怎么做 是有区别的. 尾递归可以不需要迭代的特殊形式 而仅用函数调用 就实现迭代. 局部变量, 内部定义块结构, 作用域 都能有效的 增加代码的维护性.

猜你喜欢

转载自www.cnblogs.com/flameNoodleKing/p/9551679.html