[The Little Schemer] 08 Lambda the Ultimate

Posted by roife on Sun, Jan 5, 2020

Currying

  • (rember-f test? a l) 从 list l 中移除 atom a,其中 test? 是判断是否相等的函数
(define rember-f
  (lambda (test? a l)
    (cond
     ((null? l) '())
     ((test? a (car l)) (cdr l))
     (else (cond (car l)
                 (rember-f test? a (cdr l)))))))

柯里化(Curry-ing) 指传入一个参数后函数返回另一个函数,结构通常为 (lambda (a) (lambda (x) xxx))

Thanks to Moses Schönfinkel

Thanks to Haskell B. Curry

  • (eq?-c a) 返回一个函数,这个函数的作用是判断参数是否等于 c,如 (eq?-c 'a) => (lambda (x) (eq? x 'a))
(define eq?-c
  (lambda (a)
    (lambda (x)
      (eq? x a))))

(define eq?-salad (eq?-c 'salad))

这里注意,我们没有必要为这个新函数取一个名字,完全可以使用 ((eq?-c 'salad) 'tuna) 调用它

高阶函数

这里用 Curry-ing 改写一下 rember-f

(define rember-f
  (lambda (test?)
    (lambda (a l)
      (cond
       ((null? l) '())
       ((test? a (car l)) (cdr l))
       (else (cons (car l)
                   ((rember-f test?) a (cdr l)))))))) ;; 这里注意

(define rember-eq? (rember-f eq?))

照样写一个 insertL-finsertR-f

(define insertL-f
  (lambda (test?)
    (lambda (a b l)
      (cond
       ((null? l) '())
       ((test? b (car l)) (cons a l))
       (else (cons (car l)
                   ((insertL-f test?) a b (cdr l))))))))

(define insertR-f
  (lambda (test?)
    (lambda (a b l)
      (cond
       ((null? l) '())
       ((test? b (car l)) (cons b (cons a (cdr l))))
       (else (cons (car l)
                   ((insertL-f test?) a b (cdr l))))))))

发现 insertL-finsertR-f 代码非常相似,不妨合并成一个 insert-g

  • (insert-g seq) 表示返回一个 insertL 或者 insertR
(define seqL (lambda (a b l) (cons a (cons b (cdr l)))))
(define seqR (lambda (a b l) (cons b (cons a (cdr l)))))

(define insert-g
  (lambda (seq)
    (lambda (a b l)
      (cond
       ((null? l) '())
       ((eq? b (car l)) (seq a b l))
       (else (cons (car l)
                   ((insert-g seq) a b (cdr l))))))))

(define insertL (insert-g seqL))
(define insertR (insert-g seqR))

事实上,可以直接用 lambda 传入 seq

(define insertL
  (insert-g (lambda (a b l)
              (cons a (cons b (cdr l))))))

现在回头看 subst

(define subst
  (lambda (a b lat)
    (cond
     ((null? lat) '())
     ((eq? b (car lat)) (cons a (cdr lat)))
     (else (cons (car lat)
                 (subst a b (cdr lat)))))))

会发现这个函数的结构和 insert-g 十分相似,区别在于 (cons a (cdr lat)),因此我们可以用 insert-g 定义 subset。同样我们还可以定义 rember

(define subst
  (insert-g (lambda (a b l)
              (cons a lat))))

(define rember
  (lambda (a l)
    ((insert-g (lambda (a b l) l))
     #f a l))) ;; 注意这里的 #f 用于占位

这里将函数主体从具体函数中抽象出来。

这和前面的 value 一样,value 的 o+,o*,^ 代码完全一样,区别在于 operator 不同, 所以可以把这个函数结构抽象出来

(define atom-to-function
  (lambda (x)
    (cond
     ((eq? x '+) o+)
     ((eq? x '*) o*)
     (else ^))))

(define value
  (lambda (aexp)
    (cond
     ((atom? aexp) aexp)
     (else ((atom-to-function (car aexp))
            (value (1-sub-exp aexp))
            (value (2-sub-exp aexp)))))))

同理写一下 multirember-f

(define multirember-f
  (lambda (test?)
    (lambda (a lat)
      (cond
       ((null? lat) '())
       ((test? (car lat))
        ((multirember-f test?) a (cdr lat)))
       (else (cons (car lat)
                   ((multirember-f test?) a (cdr lat))))))))

(define multirember-eq? (multirember-f eq?))

还可以把变量 a 也放入 eq? 中,如令 eq?-tuna 为 一个判断元素是否为 'tuna 的函数

(define multiremberT
  (lambda (test? lat)
    (cond
     ((null? lat) '())
     ((test? (car lat))
      (multiremberT test? (cdr lat)))
     (else (cons (car lat)
                 (multiremberT (cdr lat)))))))

Continuation

(define multirember&co
  (lambda (a lat col)
    (cond
     ((null? lat)
      (col '() '()))
     ((eq? (car lat) a)
      (multirember&co a (cdr lat)
                      (lambda (newlat seen)
                        (col newlat
                             (cons (car lat) seen)))))
     (else (multirember&co a (cdr lat)
                           (lambda (newlat seen)
                             (col (cons (car lat) newlat) seen)))))))

(define a-friend
  (lambda (x y) (null? y)))

这被称为 collector 或者 continuation

2021.03.02 备注

什么是 continuation? Continuation 就是将当前程序运行的上下文(运行时情况)保存下来,以后需要的时候再恢复。在这里对应的则是用闭包来保存程序的上下文。

分析代码会发现每次递归变化的是函数 col,每次递归改变的信息也是存储在 col 里面,每递归一次 col 就多了一层, 最后会用所有信息进行计算。即将数据和状态用闭包存储。

  • (multirember&co a lat col) 将等于 a 的元素放到列表 l1,把不等于 a 的元素放到列表 l2,最后计算 (col l1 l2)
(define multiinsertLR&co
  (lambda (new oldL oldR lat col)
    (cond
     ((null? lat) (col '() 0 0))
     ((eq? (car lat) oldL)
      (multiinsertLR&co new oldL oldR (cdr lat)
                        (lambda (newlat L R)
                          (col (cons new (cons oldL (cdr newlat))) (add1 L) R))))
     ((eq? (car lat) oldR)
      (multiinsertLR&co new oldL oldR (cdr lat)
                        (lambda (newlat L R)
                          (col (cons oldR (cons new (cdr newlat))) L (add1 R)))))
     (else (multiinsertLR&co new oldL oldR (cdr lat)
                             (lambda (newlat L R)
                               (col (cons (car lat) newlat) L R)))))))
  • (evens-only* l) 从 l 中移除所有奇数
(define even? (lambda (n) (= 0 (remainder n 2))))

(define evens-only*
  (lambda (l)
    (cond
     ((null? l) '())
     ((atom? (car l))
      (cond
       ((even? (car l)) (cons (car l) (evens-only* (cdr l))))
       (else (evens-only* (cdr l)))))
     (else (cons (evens-only* (car l))
                 (evens-only* (cdr l)))))))
  • (evens-only*&co l) 从 l 中移除所有奇数,同时计算所有奇数的和与偶数的乘积
(define evens-only*&co
  (lambda (l col)
    (cond
     ((null? l) (col '() 1 0))
     ((atom? (car l))
      (cond
       ((even? (car l))
        (evens-only*&co (cdr l)
                        (lambda (newl p s)
                          (col (cons newl (car l))
                               (* p (car l))
                               s))))
       (else (evens-only*&co (cdr l)
                             (lambda (newl p s)
                               (col newl
                                    p
                                    (+ s (car l))))))))
     (else (evens-only*&co (car l)
                           (lambda (al ap as)
                             (evens-only*&co (cdr l)
                                             (lambda (dl dp ds)
                                               (col (cons al dl)
                                                    (* ap dp)
                                                    (+ as ds)))))))))) ;; 注意这个 collector

(define the-last-friend
  (lambda (newl p s) (cons newl (cons p s))))

(evens-only*&co '((9 1 2 8) 3 10 ((9 9) 7 6) 2) the-last-friend)