《计算机程序的构造和解释》学习笔记——过程抽象

    这篇博文里说到程序=数据+过程,数据分为不同类型,每种类型有不同的操作过程。例如,两个指针变量相加是无意义的,所以对指针类型来说加法操作是“不允许”的。在汇编层,每一个数据,每一步数据操作过程都是原子级的,更高层次的数据和操作概念需要“高级”语言的支持,也就是这个语言配套的编译器/解释器能将这个“新”类型数据、“新”过程解释/编译成底层概念,才能实际运行。这里的两个“新”字就体现了程序的抽象机制。程序抽象可以“粗暴”的分为两类:过程抽象和数据抽象。之所以说粗暴是因为数据和过程的界限有时是模糊难以清晰划分的。
    计算机里的过程是对真实世界过程的抽象,在现实中需要处理事务、需要绘画、需要传递信息、需要运算……。 这些在计算机上都可以完成,对应的有计算机辅助设计、email、IM、大型运算中心等等。这些过程的操作对象就是真实世界的数据抽象。这些数据或多或少、或显著或隐晦的表示着真实世界的某种状态。数据抽象和过程抽象共同模拟了真实的世界,在现代社会的各行各业,计算机技术都在发挥着作用,助推人们更好更便捷的处理任务。而编程语言就是描述“如何做”的工具,工具反过来也会影响“如何描述”和“如何做”。不同语言有各自的特点,但目标是一样。就像自然语言一样,发音、字形、语法不同,但都是人类沟通的工具。语言反过来也会影响人的思维。计算机语言也有同样的效果,它影响程序员的编程思想,所以程序语言也在不断进化中。
    任何语言都必须包含三种机制:
  • 基本表达式,用于表示最简单的个体
  • 组合的方法,通过组合方法可以从简单的元素构造复合的元素
  • 抽象的方法,保证可以将复合对象作为单元去操作的机制。
所有的表达式都有值,程序的运行过程即是表达式的求值过程。数据表达式的值就是数据的表示方式,简单数值表达式如25,它的值就用整数25表示,复合表达式如结构体struct,它的值就是这个结构体内的数据表示形式;过程表达式的值就是对数据的“操作”,也就是过程体,如“+”加法表达式,它的值就是将它左右(或者其他,不同语言的表示方式不同)的其他表达式的值做相加操作。
/**
*
*scheme语言举例
*自定义了一个加法过程,它接收两个参数,相加并将结果返回
*下面是求值组合表达式,它的左边运算符就是刚才定义的过程
*参数1为另一个复合过程( + 1 2 ),参数2为最简单数值表达式
*/
( define mysum
    ( lambda ( a b ) ( + a b ) ) )

( mysum ( + 1 2 ) 3 )
这段程序就包含了上面描述的三个机制,define代表了一种抽象的方法,它表示自定义了一个过程,它类似于重写了加法,但这里mysum只允许两个参数;1、2、3和“+”是基本的表达式;而(+ 1 2)是组合的方法,将基本的表达式组合起来,它的值就是将“+”加法运算符运用于操作数1、2上得出的结果。

过程抽象

    上面描述了第一层次的过程抽象:将为实现目的的若干操作封装成一个过程,使得过程的使用者不需要了解该过程内部实现细节,而只要了解过程的接口,传入适当的参数,就能得到预期的结果。这里的define规则给该过程命名,mysum就如同“+”、“-”、“*”、“/”等内部过程一样,可以看作自定义的运算符,在表中的最左位置,它的值就是作用于其他数据对象的结果。
    下面考虑另一个层次的抽象问题,定义以过程作为参数或者返回值的过程。对比下面的代码:
/*
*求和公式
*/

( define square
    ( lambda ( x ) ( * x x ) ) )
( define cube
    ( lambda ( x ) ( * x x x ) ) )
( define sum-square
    ( lambda ( a b )
        ( if (> a b)
            0
            ( + (square a) ( sum-square ( + a 1 ) b ) ) ) ) )
( define sum-cube
    ( lambda ( a b )
        ( if (> a b)
            0
            ( + (cube a) ( sum-square ( + a 1 ) b ) ) ) ) )
上面的代码定义了四个过程,其中square(x)、cube(x)分别计算x的平方和立方,sum-square(a b)、和sum-cube(a b)计算从a到b的平方和与立方和。仔细看会发现求和的过程中的模式很相似。求平方和的过程中,先比较a是否大于b,如果大于b则返回0,否则求a的平方,然后递归调用sum过程,并将这两个结果相加,递归的过程中参数a加1。求立方和的过程就是在求a的立方时不同,其他部分很相似。这里面是否隐藏了某种模式,可以综合处理上述两个过程?
( define <name> 
    ( lambda ( a b )
        ( if ( > a b )
             0
             ( + ( <term> a )
                   ( <name> ( <next a) b )))))
上面的代码就是计算平方和与立方和的公共模式,term、name和next是需要填入的过程,以满足新的求和公式。这就是第二次的过程抽象,以过程作为过程的参数,使得语言编程的威力大增。
    在《计算机程序的构造和解释》第一章求方程的不动点例子中,将过程的抽象再提高一个层次。所谓不动点就是满足x=f(x)方程的x值。求解的方法就是首先猜测一个值x,然后计算f(x)、f(f(x))、f(f(f(x)))...,直到值的变化不大,就找到它的不动点了。看代码:
( define tolerance 0.00001)
( define ( fixed-point f first-guess)
    ( define ( close-enough? v1 v1 )
        ( < (abx ( - v1 v2 )) tolerance))
    ( define ( try guess )
        ( let (( next ( f guess )))
            ( if ( close-enough? guess next)
                 next
                 ( try next ))))
    ( try first-guess))
上面的代码在fixed-point过程内部定义几个辅助过程,首先是close-enough?会比较v1和v2是否足够接近;然后在try过程中,给next赋值为f(guess)的值,然后比较guess和next是否接近,如果是则返回next,否则继续求f(next)的值,直到close enough。




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值