• 不应滥用named let


    > (define (f x) x)
    > (define (g x) (let rec((x x)) x))
    > (define a '(1 2 3))
    
    > (f a)
    (1 2 3)
    > (eq? a (f a))
    #t
    > (eq? a (g a))
    #t
    
    > (define b (g a))
    > (set-car! b 10)
    > a
    (10 2 3)
    > 

    可见,g函数的定义中,named let并未深拷贝x的值,它只是建立传入参数的引用而已.

    那这也说明,如果一个函数所有参数在递归过程中都会发生改变,那么一般没必要用named let.

    又如:

    ;切割named let版 (1 2 3 4) 2 -> (3 4) 
    (define (tail x n)
      (let recur ((x x)(n n))
        (if (null? x)
            '()
            (if (> n 0)
                (recur (cdr x) (- n 1))
                (cons (car x) (recur (cdr x) (- n 1)))))))
    
    ;切割原始版
    (define (tail x n)
      (if (null? x)
          '()
          (if (> n 0)
              (tail (cdr x) (- n 1))
              (cons (car x) (tail (cdr x) (- n 1))))))

     原始版就是更好的.

    但是,对于递归过程中函数所有参数都在变化的情形,有两种情况例外,仍然需要named let:

    (1)函数要求返回递归过程中的某些变量的最终状态.例如求一个list的元素个数的函数定义:

    (define (len x)
      (let recur ((x x)(y 0))
        (if (null? x)
            y
            (recur (cdr x) (+ y 1)))))

    由于需要一个变量来记录(cdr x)的次数,因此在内嵌函数recur中增加一个参数y来实现是非常合适的.

    (2)处理不定参数的情形.这个可以用map来作非常好的说明:

    (define (imap f x . y)
      (if (null? y) 
          (let recur ((x x)) 
            (if (null? x) 
                '() 
                (cons (f (car x)) (recur (cdr x))))) 
          (let recur ((x x) (y y)) 
            (if (null? x) 
                '() 
                (cons (apply f (car x) (imap car y)) (recur (cdr x) (imap cdr y))))))) 

    显然,结合条件判断,named let能够将复杂情形转化为简单情形.思路是:

    如果y是空表,那么imap等于一个只有1个参数recur函数.否则就等于2个参数的recur函数.而后者的参数传递过程中,我们又需要用到1个参数的情形:

    (imap car y)
    (imap cdr y)

    这真是非常精妙的.

  • 相关阅读:
    从程序员到项目经理(1)
    从程序员到项目经理(26):项目管理不能浑水摸鱼
    职场“常青树”越老越值钱
    阿里巴巴离职DBA 35岁总结的职业生涯
    CEPH RGW多 ZONE的配置
    CEPH 对象存储的系统池介绍
    Windows 下配置 Vagrant 环境
    vagrant 创建虚拟机时遇到问题
    浅谈Ceph纠删码
    磁盘缓存
  • 原文地址:https://www.cnblogs.com/xiangnan/p/3392293.html
Copyright © 2020-2023  润新知