Chapter 1 构造过程抽象 [《SICP》 笔记]
来源:互联网 发布:公司财务报表软件 编辑:程序博客网 时间:2024/06/03 15:57
构造过程抽象
- 编写程序(procedure)指导过程(process)的进行
- 设计良好的计算系统,具有某种模块化的设计,各部分能独立构造、替换、排除错误。
- Lisp
- 表处理list processing,设计它是为了提供符号计算的能力,如微积分;
- 处理一种特定形式的逻辑表达式(递归方程)
- 最重要的一个特征是:process的Lisp描述(称为procedure)本身又可以做为Lisp的数据了表示和操作
- 填平"被动的"数据 和 "主动的"过程 之间的传统划分
- 程序设计的基本元素
- 程序设计语言不仅指挥计算机,更是一种框架,让人在其中组织思想
- 都有将简单认识组织成更复杂认识的方法,3种机制:
- 基本表达式,最简单的个体
- 组合的方法,从简单东西出发构造出复合的元素
- 抽象的方法,给复合对象命名,当作单元去操作
- 程序设计中,处理两类要素:过程和数据(并不严格分离);简单说,数据是操作的"东西",而过程是操作数据的规则的描述
- 对数据和过程都要提供上述3中机制
- 表达式
- 基本表达式
- 456
- 组合式
- (+ 123 345)
- 一对括号括起的一个表,最左运算符,其余是运算对象
- 求值就是将运算符表示的过程 用于运算对象的值(实际参数)
- 前缀表示
- 优点,使用可带任意个实参的过程,而没有歧义
- 优点,易嵌套,允许组合式元素本身又是组合式
- 基本表达式
- 命名和环境
- 命名
- (define var 2)
- 名字标识符称为变量,它的值为它所对应的对象
- define是最简单的抽象方法,允许用简单名字去引用 组合运算的结果
- 实际上,构造程序,就是逐步创建出越来越复杂的计算性对象;解释器提供这种便利,并促进采用递增方式去开发调试
- 环境
- 将值与符号关联,解释器必须维护名字-值对偶;这种存储就成为环境;一个计算过程中可能有多个不同环境
- 命名
- 组合式的求值
- 组合式一般性求值规则
- 求值各子表达式;
- 将最左子式的值,应用于所有其他子式的值
- 递归,树形积累
- 这一规则,是递归的,有调用规则本身的需要
- 可用一棵树来表示求值过程,结点为组合式,发出分支对应子式;求值就是从端点向上穿行组合,进行"树形积累"(每个节点都把孩子收到一起)
- 反复用步骤一,直到不再遇到组合式而是基本表达式,取出数和名字的值
- 特殊形式
- 如define((define var 2)不是组合式),有自己的求值规则,这些不同的表达式(及相应求职规则)组成语法形式
- 复合过程
- 过程定义的一般形式:(define ( ) )
- 过程定义,强大的抽象技术,为复合操作提供名字,以作为单元使用,然后使用就与基本过程完全一样。
- 过程应用的代换模型
- 过程应用的代换模型,实际是一种计算过程,可用于确定过程应用的"意义"
- 只是帮助人领会,并非解释器实际工作方式;实际中不会去操作过程正文,去代换;而是利用局部环境,产生"代换"的效果。
- 只是第一个简单模型,后面会不适合,还会有更多精化的模型
- 应用序和正则序
- 除 前面讲到的求值规则(应用序),还有正则序求值,用表达式去代换形参,直到只剩基本式再计算求值;完全展开后再归约。
- 前面讲到的求值规则 成为 应用序求值,解释器实际采用的,先求值参数而后应用
- 能用替换模拟的,两序结果相同;应用序可避免重复计算,且当替换模型不可用时,正则序很麻烦,所以一般用应用序
- 组合式一般性求值规则
- 条件表达式和谓词
- cond,if
- (cond ( ) ( )...),依次看谓词pi,直到某谓词为真
- else特殊符号,用在cond最后一句,永远返回,即else后谓词永为真
- if是cond只有两情况的特例;形式(if )
- 逻辑符合运算符
- and,or,not
- cond,if
- 练习
- ex1_1:略
- ex1_2:略
- ex1_3:略
- ex1_4行为是:a+|b|
- ex1_5若是应用序会死循环,反复不断求表达式(p); 若是正则序,结果0,代换所有表达式,(p)还没有求就结束了。
- 实例:采用牛顿法求平方根
- 说明与行动,数学函数与计算过程(有趣的观点)
- 平方根函数定义:根号x = y,使得y>=0且y^2=x,正统的数学函数,能推导出一些关于平方根的一般性事实;但并没有描述计算过程,给一个x,如何找到y
- 数学函数与计算过程的差异是说明性知识 与 行动性知识 间的普遍性差异的一个具体体现
- 数学关心说明性的描述(是什么),而CS关心行动性的描述(怎么做)
- 求平方根的牛顿法
- 已有猜测y, 新猜测为 y 与 x/y 的平均值,该值会更接近实际的平方根?如何证明啊?
- 练习
- ex1_6:if,cond都是特殊形式,与一般求值规则不同;定义的new-if没有短路性,所有子表达式都会计算,而死循环。
- ex1_7:对很大和很小的y,good-enough?不用固定差值了,而用相对值,用变化和猜测值的比率(绝对值和相对值)
- ex1_8:略
- 说明与行动,数学函数与计算过程(有趣的观点)
- 过程作为黑箱抽象
- 过程抽象
- sqrt程序可被分为多个过程,每个有自己明确的工作,且可被其他过程使用
- 像square与其说是一个过程,不如说是一个过程抽象;任何能计算出平方的过程都可用
- 一个过程定义需要能隐藏起一些细节,能被当作黑箱使用,不用弄清其中的具体实现。
- 局部名
- 约束变量,形式参数的名字,具体是什么不重要,只要在体内统一,体为其作用域
- 自由变量,非约束变量
- 内部定义和块结构
- 对用户由暴露必要的过程,其他都定义到该过程内部;这种嵌套定义,称为块结构,解决名字包装(减少冲突)问题
- 部分参数传递就不必要了,可以改为内部定义中的自由变量,这种方式称词法作用域(省去很多参数传递)
- 过程抽象
- 过程与它们产生的计算
- 知道一些基本语法,但不知常见模式,就像下棋只知移动规则,却不知典型开局,战术和策略;需要各种棋步的价值(值得定义那些过程)的知识,需要对后果(执行过程效果)做出预期的经验;只有能visualize 各种 procedures后,才能成为专家。
- 一个过程也就是一种模式,描述the local evalution of a computational process,即specifies how each stage of 程序 is built upon the previous stage.我们想评价整个程序whose local evolution 由 procedure specify。
- 线性的递归和迭代
- 以求n!为例
- 相同 of 两processes
- 可用substitution model 展开;计算步骤数都正比于n;求积的次序1,2,3...也是一样的
- 不同的"shape" of two processes,evolve很不一样
- recursive process,由代换模型看出,是先扩展,再计算收缩;扩展时建立了a chain of deferred operations;chain的长度正比于n(因此称线性);解释器需要维护额外信息,而不是仅靠程序变量,chain越长需要维护的信息却多
- iterative process,不grow和shrink,任何时刻的state仅由a fixed number of state variables 来 summarized,以及一个更新变量规则,一个终止条件;暂停后,只需要给解释器那几个变量就可继续,不用其他信息(迭代给够要保持的状态变量,递归并没有在这种意义上保持这些状态)
- 区分recursive process 与 recursive procedure
- recursive procedure,语法上的,定义自己时又用了自己
- recursive process,模式,evolve,而不是 the syntax of how a procedure is written,它会消耗n正比的mem
- 而scheme无此缺陷,只要是iterative process 一定常数空间,即使描述为recursive procedure;利用 tail-recursive,使常规过程调用能express迭代
- 练习
- ex1_9分辨是递归还是迭代,可用代换模型,也可直接看"自己"过程是否被"最后"调用,若是就是迭代,不会有没计算的操作(完全和循环又跳转到开头一样)
- ex1_10(A 0 n) (A 1 n) (A 2 n)这几个要依次求,求后面的会用到前面的,也算建立抽象了,否则很难算出;另外(A 3 n)类似4维空间一样,不能直观描述了,手上的数学工具不便描述
- 树形递归 tree recursion
- Fib(n)
- 直接根据定义来,很易写,但prcess的步数随n指数增长,而所占空间正比于n(指栈的最大深度,一般树形递归的步数都是指数,而空间线性)
- iterative process,用变量a,b,对应初态Fib(1),Fib(0),然后不断更新;计算步数与n成线性(就是动态规划)
- 有几种找零方式
- 定义为recursive procedure,为tree-recursive processes 易于理解和设计;划分为两组,包含某一零钱或不包含,树形展开,问题规模不断变小,直到变成degenerate cases。
- 练习
- ex1_11为写成iterative的形式,需要增加几个变量,同时让fz_iter"最后算"
- ex1_12打印出帕斯卡三角,下行元素是上行两个之和(写递归,关键是递归关系,=右侧看作已知量,定义中对自身的调用看作变量;然后接是返回条件)
- ex1_13...关于Fib(n)的值,未证...
- Fib(n)
- 增长的阶
- n,R(n)
- n是对the size of the problem的度量,R(n)是process所需资源
- n和R(n)可对应各种东西,如时间,内存等
- R(n)=Θ(f(n)),读作"theta of f(n)"
- k1*f(n) <= R(n) <= k2*f(n)
- 增长的阶只是粗略描述,但同时又提供了 a useful indication of 问题 size 改变对process行为的影响
- 练习
- ex1_14:空间O(n),因为每次存根到当前处的路?时间是指数吧,还是和其他什么有关?
- ex1_15:略
- n,R(n)
- 求幂exponentiation
- b^n
- 利用b^n=b*b^(n-1),且b^0=1,若写为recursive process,则时空消耗都为Θ(n);若写为iterative process,则时为Θ(n),空变为Θ(1).
- 而若利用b^n=(b^(n/2))^2,当n为偶;b^n=b*b^(n-1),当n为奇,则可得O(lgn)
- 练习
- ex1_16 a*b^n=a((b^2)^(n/2)); a*b^n=b^(n-1)*b*a=b^(n-1)*A; b^0 = 1; 参数a,b就是状态(ab^n为不变量,不变量,设计迭代算法的有力工具)
- ex1_17 完全类比fast-expt
- ex1_18 结合1.16和1.17,a*b+acc,初始acc=0,最后返回acc
- ex1_19 建立a2,b2与a0,b0的关系,然后对比形式就得p',q'与p,q的关系了(Fib(n+1)可看作对(0,1)的T^n次变换得(Fib(n),Fib(n+1)),可用连续平方变换去求T^n(注意T是一个变换;不仅数,过程也可用连续平方来思考)
- b^n
- 最大公约数
- Euclid's Algorithm辗转相除 a=bc+r
- 若a/b = c...r,那么GCD(a,b)=GCD(b,r);因此可以不断变小问题,直到r=0
- 增长阶为Θ(logn),基于lame's定理得到增长阶(对定理就那么一提)
- 练习
- ex1_20 代换进去,应用序4次,正则序7次
- Euclid's Algorithm辗转相除 a=bc+r
- 素数检测
- 搜索因子
- 从[2,sqrt(n)]搜索检测因子,若无则是素数
- 取sqrt(n)终止的原因是,设n的因子的,则另一因子为n/d,其中必有一个<=sqrt(n),否则两因子的积>n.
- 从[2,sqrt(n)]搜索检测因子,若无则是素数
- 费马检测
- 费马小定理:若n是素数,a是任一<n的整数,则a^n与a同余
- 外加一补充,若n不是素数,大部分a将不满足同余关系
- 因此得到算法,随机选a,判定,如不满足一定不是素数;若满足,则很可能是素数
- Probabilistic methods
- Fermat test与之前的算法不同,不保证答案正确,而是 probably correct.
- 错误来自两方面
- 未全检测
- 存在不是素数但满足性质的数
- 但同时Fermat test 随检测次数(尝试的a的个数)增加,可让错误的概率减小到任意程度
- 练习
- ex1_21 直接调用运行完事
- ex1_22 runtime返回微秒时间;还有newline和dispaly;通过实际运行时间验证增长率(这种方法也许有时可用)
- ex1_23 测试了2就不用再测试其他的偶数了,可以使运行速度减半(小技巧大幅提升速度)
- ex1_24 验证费马检测的增长率,确实很快
- ex1_25 expmod利用了 xmodm * ymodm = (x*y)modm,避免了溢出和对很大的数的乘法,对任意精度乘数太大会变慢(很重要和常用的方法)
- ex1_26 用*需要两个因子,虽然两因子相同但是都会算(注意消除不必要的计算)
- ex1_27 略
- ex1_28 Miller-Rabin不会被欺骗,对Fermat test的改进
- 搜索因子
- 用高阶函数做抽象
- (* x x x)能算立方,但却不能表述立方这一概念;需要能为公共的模式命名,建立抽象,而后直接在抽象的层次上工作;过程提供了这种能力。
- 高阶过程(procedure),以procedures作为参数,或者返回procedures。若限制为数字有局限性,因为有些相同模式使用不同过程。高阶表达这种概念。例如,∑f(n)中的f(n)为参数,∑为高阶过程
- 过程作为参数
- 类似 ∑f(n) = f(a)+...+f(b),不仅a,b是参数f也是参数
- 在有了以f作为参数的sum之后,就可use it a building block in formulating further concept.比如求定积分
- 练习
- ex1_29 用辛普森规则求定积分,公式变了,但还是可以看做sum形式(抽象模式,要从已有的模式出发思考)
- ex1_30 把递归的sum变成迭代的sum,有个result一直在传递,且最后返回这个result
- ex1_31 定义product,类似sum,只是+变成*;然后将product应用于计算pi的近似式(注意这种分层抽象,习惯这种抽象;还有自顶向下和自底向上思考)
- ex1_32 product是一种更一般的模式accumulate的特例,以+,*为参数
- ex1_33 给accumulate增加概念filter(问题空间中思考,概念上的分离,抽象能力)
- 用labmda构造过程
- lambda
- 形式(lambda ( )
- 与define create procedure 的方式一样,只是lambda没有名字,即区别只是 the result procedure 在环境中没有名字
- let
- 有时需要局部变量,可以用define建立一个辅助过程来约束这些变量;还以用lambda建匿名过程,局部变量在匿名过程的参数上
- 还以用特殊形式let,其一般形式为 (let (( <exp1)...( <expn)) )
- let仅仅是lambda的语法外衣
- 练习
- ex1_34 ?f是名字,其值对应相应的lambda?
- lambda
- 过程作为一般性的方法
- 两种过程
- 复合过程(就是define),将若干操作的模式抽象出来,使描述的计算不再依赖特定的数值.
- 高阶过程,表述计算的一般性过程,与特定函数无关
- 区间折半求方程的根
- 求f(x)=0的根,利用正负之间必有0点,不断试解,并根据符号缩小范围
- 运行时间Θ(log(L/T)),其中L是区间长度,T是误差范围(感觉L/T就是离散化)
- 找出函数的不动点
- 不动点f(x)=x,通过f(x), f(f(x)), f(f(f(x)))... 直到变化不大,只对某些函数适用,例如,对sin(x)=x能正常工作
- 用来求平方根
- 解y^2=x, 给定x, 有 y=x/y, 即f(y)=x/y=y;
- 用上述方法,直接求不动点,会不收敛而无限循环;
- 解决办法,不动点在y1与x/y1之间,猜个中间1/2*(y1+x/y1),即把y=x/y,变成求y=1/2(y+x/y)(恒等式变形,就解决问题)
- 这种用平均值去逼近一个解的方法陈伟平均阻尼(就是控制震荡,让猜测变化不太剧烈)
- 练习
- ex1_35 黄金分割点就是 x^2= x+1的解,恒等式就证明了;直接带入函数计算
- ex1_36 平均阻尼快些,不用阻尼小有明显震荡
- ex1_37 (a)大概k=13; (b)递归从i算到k,而迭代需要从k算到1才行(改成迭代还要改变计算顺序)
- ex1_38 就是一样求连除式
- ex1_39 同上
- 两种过程
- Procedures as Return Values
- 平方根
- 可将不动点搜索,平均阻尼,函数y=x/y,这3种思想结合到一起来求平方根
- 即,(fixed-point (average-dump (lambda (y) (/ x y))) 1.0)
- 这样表述出来的procedure 与前面没有划分的会产生同一个process; 一般一个process可变为多种prcedures, 而有经验的程序员能设计出好的procedure, 清晰的易理解, process能分为有用的相互分离的个体,使这些个体能用于别处。(模块化)
- 牛顿法
- (fixed-point (newton-transfor g) 1.0), 将求g(x)=0的解,转化为求 x= x - g(x)/Dg(x)的不动点
- 抽象和第一级抽象
- 实际上上面是求平方根的两种不动点形式,每种都是从一个函数出发g, 然后变换产生f, 接着去找f的不动点,因此可以统一出一种模式
- 程序员应alert to opportunities to identify the underlying abstractions in our programs 然后build upon them 接着 generalize them 以 create more powerful abstraction
- 能这样抽象,就为将概念用于其它地方做好了准备;但也并不是要尽量抽象,而是要适度选择level.
- 高阶过程使能把这些抽象显示表示为programing langurage 中的基本元素,就像其他可计算元素一样
- 第一级元素,使用方式限制最少的元素
- 他们可以用变量命名,作为过程参数和返回,可包含在数据结构中;
- Lisp不同于很多语言,它给了过程完全的第一级状态
- 实际上上面是求平方根的两种不动点形式,每种都是从一个函数出发g, 然后变换产生f, 接着去找f的不动点,因此可以统一出一种模式
- 练习
- ex1_40 解3次方程的0点
- ex1_41 double连续k次,会调f 2^k次
- ex1_42 实现复合函数compose,将f和g合并为f(g(x))
- ex1_43 repeated
- ex1_44 平滑,多次平滑
- ex1_45 n次根 与 平均阻尼次数问题
- ex1_46 高阶过程,本章的数值算法都是迭代式改进
- 平方根
0 0
- Chapter 1 构造过程抽象 [《SICP》 笔记]
- Chapter 1 构造过程抽象
- SICP学习笔记及题解---构造过程抽象(一)
- SICP学习笔记及题解---构造过程抽象(二)
- SICP学习笔记及题解—构造过程抽象(三)
- SICP第一章——构造过程抽象之程序设计的基本元素(1.1)笔记及习题解答
- SICP 读书笔记——第 一 章 构造过程抽象——第 1 节 程序设计的基本元素
- SICP第一章——构造过程抽象之过程与它们所产生的计算(1.2)笔记及习题解答
- SICP(二):过程抽象
- SICP 读书笔记——第 一 章 构造过程抽象——第 3 节 用高阶函数做抽象
- SICP学习笔记:用高阶函数作抽象(1)
- SICP Chapter 1 习题试解
- SICP 读书笔记——第 二 章 构造数据抽象——第 1 节 数据抽象导引
- SICP 读书笔记——第 二 章 构造数据抽象
- Chapter 2 Building Abstractions with Data [《SICP》笔记]
- Chapter 3 模块化、对象和状态 [《SICP》笔记]
- SICP学习笔记(1)
- SICP学习笔记(1):
- 在windows上安装easyphp和zencart
- Java Web开发使用配置文件链接数据库
- Binary Tree Zigzag Level Order Traversal
- kruskal算法
- 第二章 线性表 知识导图
- Chapter 1 构造过程抽象 [《SICP》 笔记]
- 数据结构第一章知识结构图
- try与catch异常捕获处理(说明)及案例
- 专题:基于ARM的嵌入式Linux系统开发主讲:刘老师
- 数据库范式
- 机器人行业
- 小黑小波比.保存密码加密方式
- Unix时间戳转FileTime
- C# λ运算符=>匿名方法 lambda表达式