cps continuation passing style

摘录自:

https://cgi.luddy.indiana.edu/~c311/lib/exe/fetch.php?media=cps-notes.scm

怎样理解 Continuation-passing style? - 知乎

#|

CPS Lecture

No published books do this subject justice (including Dan's!)

Which part of (f (g (h i) j) k) can be done first? (h i), since it
must be evaluated before (g (h i) j) can be applied.

What about (f (g (h i) (j k)))? Scheme doesn't specify the order in
which arguments are evaluated so it could be either (h i) or (j k).

So, let's take control. (h i (lambda (hi) ...)) We assume that hi is
the result of applying (h i). Then, we drop in everything else that
has to be done to replace the ...:

(f (g (h i) (j l)))

becomes

(h i (lambda (hi) (f (g hi (j l)))))

(lambda (hi) (f (g hi (j l)))) is a continuation. hi only appears once
in the body of the continuation, because hi is intended to replace (h
i) and only (h i).

Let's write rember in CPS. First, the direct style:

(define rember8
  (lambda (ls)
    (cond
      [(null? ls) '()]
      [(= (car ls) 8) (cdr ls)]
      [else (cons (car ls) (rember8 (cdr ls)))])))

First rule: whenever we see a lambda in the code we want to CPS, we
have to add an argument, and then process the body:

(lambda (x ...) ...) => (lambda (x ... k) ...^)

Let's start by adding a k to the outer lambda:

(define rember8
  (lambda (ls k)
    (cond
      [(null? ls) '()]
      [(= (car ls) 8) (cdr ls)]
      [else (cons (car ls) (rember8 (cdr ls)))])))

Now, to handle the rest of the program, we have to introduce a new rule.

***

Second rule: "Don't sweat the small stuff!"

Small stuff is stuff we know will terminate right away.

Don't sweat the small stuff if we know it will be evaluated.

Don't sweat the small stuff if it *might* be evaluated, but instead
pass it to k.

***

A good example of the first is (null? ls) in the first cond line. We
know it will be evaluated, and we know it's small stuff, so we don't
have to worry about it.

What about the '() that's returned as an answer? The other part of the
second rule is that if some small stuff *might* be evaluated, we just
pass it to k.

After applying the second rule to the first cond line, we get:

(define rember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (cdr ls)]
      [else (cons (car ls) (rember8 (cdr ls)))])))

The second cond line also has small stuff in both the test and the
return value, so we can treat it just like the first line.

(define rember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (k (cdr ls))]
      [else (cons (car ls) (rember8 (cdr ls)))])))

The else case, however, does *not* have small stuff as a return value,
so we have to build a new continuation:

(define rember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (k (cdr ls))]
      [else (rember8 (cdr ls) (lambda (x) (cons (car ls) x)))])))

We're not quite done, though, since we now have small stuff in the
body of the continuation. So, we just pass it to k:

(define rember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (k (cdr ls))]
      [else (rember8 (cdr ls) (lambda (x) (k (cons (car ls) x))))])))

This is now completely CPSed, but how do we invoke it? After all, we
need a k to pass in. Since (rember8 '() k) should be '(), k can be the
identity function (lambda (x) x):

> (rember8 '(1 2 8 3 4 6 7 8 5) (lambda (x) x))

What properties can we observe about this program?

First, all non-small stuff calls are tail calls. Here's the program
with the tail calls surrounded by asterisks:

(define rember8
  (lambda (ls k)
    (cond
      [(null? ls) (*k* '())]
      [(= (car ls) 8) (*k* (cdr ls))]
      [else (*rember8* (cdr ls) (lambda (x) (*k* (cons (car ls) x))))])))

Why don't null?, =, car, cdr, and cons count? Because they're just
small stuff, and when we combine small stuff together in small ways,
the combination remains small.

Second, all arguments are small stuff. Yep, even the lambda in the
else line, because lambda is *always* small stuff.

Notice that this is essentially a C program. All we have to do is
convert the continuations to data structures (remember how we did the
same thing with closures).

Let's trace (rember8  (lambda (x) x))

ls | k

'(1 2 8 3 4 6 7 8 5) | (lambda (x) x) = id
'(2 8 3 4 6 7 8 5)   | (lambda (x) (id (cons 1 x))) = k2
'(8 3 4 6 7 8 5)     | (lambda (x) (k2 (cons 2 x))) = k3

Once we hit the 8, we apply (k (cdr ls)) where k is k3 and ls is '(8 3
4 6 7 8 5)

(k3 '(3 4 6 7 8 5)) = (k2 (cons 2 '(3 4 6 7 8 5)))
(k2 '(2 3 4 6 7 8 5)) = (id (cons 1 '(2 3 4 6 7 8 5)))
(id '(1 2 3 4 6 7 8 5)) = '(1 2 3 4 6 7 8 5)

And we're done.

Let's try a more complicated program, multirember8. Instead of just
removing the first 8, it'll remove all of the 8s.

(define multirember8
  (lambda (ls)
    (cond
      [(null? ls) '()]
      [(= (car ls) 8) (multirember8 (cdr ls))]
      [else (cons (car ls) (multirember8 (cdr ls)))])))

Now, let's start CPSing by going back to our CPSed rember8:

(define multirember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (multirember8 (cdr ls))] ;; uh-oh!
      [else (multirember8 (cdr ls) (lambda (x) (k (cons (car ls) x))))])))

What do we need to do for the second line? Since multirember8 takes
two arguments, we need to now pass it a continuation.

(define multirember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (multirember8 (cdr ls) (lambda (x) (k x)))]
      [else (multirember8 (cdr ls) (lambda (x) (k (cons (car ls) x))))])))

But what's (lambda (x) (k x)) doing? It's taking whatever is passed to
it, and passing it to k. This whole expression, therefore, is
equivalent to k.

Eta reduction: (lambda (x) (M x)) = M if x is not free in M and M is
guaranteed to terminate. M is any arbitrary expression that satisfies
these rules; it doesn't have to be only a single variable like k.

So, whenever you see a tail call, you don't even have to think about
eta. Just pass k to it.

(define multirember8
  (lambda (ls k)
    (cond
      [(null? ls) (k '())]
      [(= (car ls) 8) (multirember8 (cdr ls) k)]
      [else (multirember8 (cdr ls) (lambda (x) (k (cons (car ls) x))))])))

|#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
社会的进步导致人们对于学习的追求永不止境,那么追求农业信息化的方式也从单一的田地教程变成了多样化的学习方式。多样化的学习方式不仅仅是需要人们智慧的依靠,还需要能够通过软件的加持进行信息化的价值体现。软件和系统的产生,从表面上来看是方便了某一行业和某一行业的用户,其实是从本质上来说是提高了社会的进步。就拿我们常见的出行而言,滴滴出行看似是为了打车和出行的人方便,但其实通过另外一种程度上来说,可以通过软件应用的调度和发展来为社会、城市出行的发展做出巨大的贡献。我们国家从最早的中国制造业演变到现在的“智造”,就是因为有软件信息系统的价值,能够将一些智慧的因素加入到制造的过程当中,而这一点就是软件系统来改变生产和现实的需求。在计算机时代日益发展的今天,计算机网络正快速融入这个社会的每一个领域。农业的发展是社会当中一种必有可少的方式。果树在种植和培养是直接影响果农及果商的发展,但在果树的资源管理方面还是有着很大的不同,所以信息多样化的果树管理方式很重要。在传统的果树资源管理上还有着很大的约束,为此开发和设计JSP杏种质资源管理系统,该系统内容丰富多彩,用户可以在线进行果杏树的资源查询等。本文还是使用JSP的方式来进行管理的,但在系统建设过程当中也考虑了许许多多信息安全的保护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值