named let 递归和闭包的利器

named let和递归,闭包联系十分密切.而且还是提高性能的重要手段.先来看一个make-list函数的模拟,最原始的写法大概是:

(define (imake-list n member)
    (if (= 1 n)
        (cons member '())
        (cons member (imake-list (- n 1) member))))

这种写法的毛病在于:

1.递归过程中,member变量可能需要在全局作用域中查找,比局部作用域查找要慢.

2.member是一个固定的参数,然而递归过程在不断重复将它传入imake-list.

这个时候你需要 named let,完美解决这2个问题:

(define (imake-list n member)
  (let recur ((n n))
    (if (= 1 n)
        (cons member '())
        (cons member (recur (- n 1))))))

(imake-list 5 "a")

具体过程:

1.let创建了一个内部函数recur.它接受一个参数,返回一个列表.

2.imake-list的n传递给了recur的n.而后者是在局部作用域.

3.递归过程转移到recur中进行,固定参数member成了它的自由变量.避免了重复传入的问题.

似乎迭代和递归会很频繁地用到这种技巧,例如很基本的函数map的模拟,也可以分为2个版本:

;原始低效版
(define (imap f x . y)
  (if (null? y)    
      (if (null? x)
          '()
          (cons (f (car x)) (imap f (cdr x))))
      (if (null? x)
          '()
          (cons (apply f (car x) (imap car y)) (apply imap f (cdr x) (imap cdr y))))))

;named let高效版
(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)))))))

(map + '(1 2 3) '(1 2 3) '(1 2 3)) 
(imap + '(1 2 3) '(1 2 3) '(1 2 3)) 
原文地址:https://www.cnblogs.com/xiangnan/p/3390253.html