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))