SICP学习笔记(3.1)

                                           SICP学习笔记(3.1)

                                                  周银辉 

 

可以明显地感觉出来,从3.1节开始,作者将我们从面向过程的编程逐渐带入到面向对象的世界中来,这比C++直接用一个class关键字来得更为精彩。

我们先看一段纯粹的过程式的编程,假设我们要存储一个人的基本信息,比如Name和Age,那么我们的代码应该如何写呢?

 ExpandedBlockStart.gifScheme版本

(define name  " nobody " )
(define age  
0 )

(define (getName) name)

(define (setName newName)
  (set! name newName))

(define (getAge) age)

(define (setAge newAge)
  (set! age newAge))

(setName 
" ZhouYinhui " )
(setAge  
25 )

(display (getName))
(display 
" 's Age is  " )
(display (getAge)) 

 哦~ 有一点似乎不太好,我们在使用name和age这样的全局变量哦,其他任何的地方也可以完全不通过上面的GetXXX SetXXX方法,也可以访问到它们。而我们希望仅能通过Get和Set方法来访问。

那么,将name和age拿到方法内部去吧!

在命令式语言中,准确地说是在不支持闭包的语言中,上面那句话可能将成为笑话:那是不可能的,每次方法调用时方法内的局部变量都是重新生成的。

为了简化代码,下面的代码都省略对name的操作,只用age进行示例。 

恩,C++,Java不行,我们就看看JavaScript是如何办到的吧:

ExpandedBlockStart.gif JavaScript
function  F()
{
    
var  age  =   25 ;

    
var  get  =   function  getAge()
                 {
                       
return  age;
                 }
     
var  set  =   function  setAge(newAge)
                 {
                        age 
=  newAge;
                 }

     
var  d   =   function  dispatch(flag)
               {
                       
if  (flag  ==   " get " )
                          
return  get;
                       
else
                         
return  set;
               }
  
return  d;
}

// 这里不能成功
(F( " set " )( 100 ));
alert( F(
" get " )() );

// 这里能成功
var  a  =  F();
(a(
" set " )( 100 ));
alert( a (
" get " )() );

 

 

 函数F是一个高阶函数,其返回的是另外一个函数d,d又是另外一个高阶函数,我们看到d将根据flag指示,返回函数get或函数set。在函数F中有一个局部变量age,由于age将被get和set使用,当执行离开F时,age并不会被立即销毁,age将和d,get,set一起形成一个闭包,被函数F返回。还记得闭包吗,可以到SICP2.2.1复习一下。这是关键点。为此,我们必须转换命令式编程语言的观点了:这里的F有Object性质,而不仅仅是一个Action,这个Object我们可以理解成返回的那个闭包。

再看看下面这段代码为啥不能达到我们的期望值(弹出的消息框显示的是25,我们希望是100) 

(F("set")(100));

alert( F("get")() ); 

既然我们说了F具有Object性质,那么我们假想F是Java中的一个class类型,那么上面的代码就形同:

(new F("set")(100));

alert( new F("get")() );

两句代码完全就是new的两个不同的对象,在前一个对象上 设置年龄,又重新new一个新对象来获取年龄,自然设置不会成功了。所以下面的代码就意识到了这一点:
var a = F();
(a(
"set")(100));
alert( a (
"get")() );

再干一件有趣的事情:C#号称支持了Lambda,高阶函数等等,那么要将上面的代码改写成C#却并不容易:

ExpandedBlockStart.gif C#
        XXX F()
        {
            var age 
=   25 ;

            Func
< int >   get   =  ()  =>   age;
            Action
< int >   set   =  (newAge)  =>  age  =  newAge;

            YYY d  =  ( string   flag)  =>
                {
                     if  ( " get " .Equals(flag))
                         return   get ;
                    
else
                        
return   set ;
                 };

            
return   d;
          }

 

 

上面的XXX与YYY处应该如何编写呢,也就是说F的返回值类型和d的类型应该是什么 ?

回到Scheme吧!

ExpandedBlockStart.gif Scheme
(define (People age name)
  (define (getAge) 
    age)
  (define (setAge newAge)
    (set! age newAge))
  (define (getName) 
    name)
  (define (dispatch method)
    (cond ((eq? method  ' getAge) (getAge))
          ((eq? method  ' setAge) setAge)
          ((eq? method  ' getName) (getName))
          ( else   " invalid method " )))
  dispatch)

(define p1 (People  25   " ZhouYinhui " ))

(p1  ' getName)
((p1  ' setAge) 100)
(p1  ' getAge)

 

 天啊,此时,我完全搞不清楚我应该将People看做是一个高阶函数还是应该看做一个面向对象的中类型。似乎我们已经踏入了面向对象的殿堂。age和name是类的私有字段,get set 则是类的私有函数,而那个dispatch完全就是一个函数分派器嘛。还记得“分派"吗,可以到这里复习一下。

 


搞清楚了上面的,练习题就是小case了:

练习3.1

(define (makeAcc ini)
  (
let  ((acc ini))
    (lambda (n)
      (set! acc (+ acc n))
      acc)))

(define F (makeAcc 
5 ))
(F 
10 )
(F 
10 )

 

 

练习3.2

ExpandedBlockStart.gif
(define (makeMonitored f)
  (
let  ((count  0 ))
    (define (reset)
      (set! count 
0 ))
    ;notes that, must add () 
to  times  to  make it  as  a method
    ;
do  NOT : (define times count)
    (define (times)
      count)
    (define (dispatch arg)
      (cond ((eq? arg 
' reset) (reset))
            ((eq? arg  ' times) (times))
            ( else
             (
begin
               (f arg)
               (set! count (+ 
1  count))))))
    dispatch))

(define m (makeMonitored sqrt))
(m 
100 )
(m 
10000 )
(m 
' times)

 

 

练习3.3

ExpandedBlockStart.gif
(define (error msg)
  (lambda (arg)
    (
begin  (display msg)
           (display 
" : " )
           (display arg)
           (display 
" \r " ))))
  
(define (makeAccount balance)
  (define (withDraw amount)
    (
if  (>= balance amount)
        (
begin  (set! balance (- balance amount))
               balance)
        
" Insufficient funds " ))
  (define (deposit amount)
    (set! balance (+ balance amount)))
  (define (dispatch pwd m)
    (cond ((
and  (eq? pwd  ' password) (eq? m  ' withDraw)) withDraw)
          ((
and  (eq? pwd  ' password) (eq? m  ' deposit)) deposit)
          (
else  (error  " invalid method " ))))
  dispatch)

(define acc (makeAccount 
1000 ))
((acc 
' password  ' withDraw)  123 )
((acc 
' password  ' deposit)   1000 )
((acc 
' invalid-password  ' withDraw)  245 )

 

 

练习3.4略掉(与3.3类似) 

 

转载于:https://www.cnblogs.com/zhouyinhui/archive/2010/02/24/1673027.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值