先得 - 经验丰富的阴谋家

时间:2017-02-04 19:58:22

标签: scheme continuations callcc seasoned-schemer

请参阅第19章中的two-in-a-row*?功能。

我的问题是关于(leave '())辅助函数中的get-first。请注意,(waddle l)将返回'()或原子,表示列表已用尽或列表中的原子被检索。

如果没有(leave '()),它仍会返回这两种值,只是不使用延续leave。但这本书说没有(leave '())是不好的,我只是看不出原因。

(define two-in-a-row*
  (letrec ([leave id] ; the identity function 
           [fill id]
           [waddle (lambda (l)
                     (cond [(null? l) '()]
                           [(atom? (car l))
                            (begin 
                              (letcc rest
                                     (set! fill rest)
                                     (leave (car l)))
                              (waddle (cdr l)))]
                           [else
                            (begin
                              (waddle (car l))
                              (waddle (cdr l)))]))]
           [get-first (lambda (l)
                        (letcc here
                               (set! leave here)
                               (waddle l)
                               (leave '()) ; why is this part needed???
                               ))]
           [get-next (lambda (l)
                       (letcc here
                              (set! leave here)
                              (fill 'go)))]
           [T? (lambda (a)
                 (let ([n (get-next 'dummy)])
                   (if (atom? n)
                       (or (eq? a n)
                           (T? n))
                       #f)))])
    (lambda (l)
      (let ([fst (get-first l)])
        (if (atom? fst)
            (T? fst)
            #f)))))

非常感谢。

关于这个问题的另一个有趣的tread

3 个答案:

答案 0 :(得分:1)

非常感谢Will Ness的例子。我去了一些更简单的。所以“那是多么糟糕?” - (leave '())中没有get-first

简答:
请注意,从我的代码中 i)每次调用leaveget-first时,都会重新创建get-next。它将返回get-firstget-next ii)fill将成为一个链,取决于之前的fill,它将始终返回get-first

示例
输入:'(1)

因此,我们首先评估get-first上的'(1) i)设置leave
ii)开始(waddle '(1)
iii)因为1是一个原子,所以将fill设置为当前继续。请注意,如果我们使用fill,那么它会转到(waddle (cdr l)),然后它将返回get-first 。 iv)使用返回值为get-first的{​​{1}}返回leave

然后我们转到评估1,然后依次运行(T? 1) i)设置get-next
ii)运行leave
iii)开始fill
iv)从(waddle '())返回(),然后返回waddle

注意
1)如果我们没有get-first,则(leave '()将返回get-first,然后'()返回two-in-a-row*。所以我们可以得到相同的答案,但行为不是我们想要的 2)如果我们拥有它,请注意#f现在是由leave创建的leave,因此它会将get-next转移到'()。<登记/> 3)当我们创建get-next时,列表中有超过1个输入,它将根据之前的fill创建,从而产生一个取决于先前fill的链。

答案 1 :(得分:0)

命名视线。我使用“yield”表示“leave”,“next”表示“fill”。我还必须定义atom?并将letcc重写为call/cc,以使其在Racket中运行。这是完整的代码:

(define two-in-a-row*
  (letrec ([yield '()] 
           [next '()]
           [atom? (lambda (x) (and (not (null? x))
                                   (not (pair? x))))]
           [waddle (lambda (l)
                     (cond [(null? l) '()]
                           [(atom? (car l))
                            (begin 
                              (call/cc (lambda ( here2 )
                                          (set! next here2)
                                          (yield (car l))))
                              (waddle (cdr l)))]
                           [else
                            (begin (waddle (car l))
                                   (waddle (cdr l)))]))]
           [get-first (lambda (l)
                        (call/cc (lambda ( here1 )
                                    (set! yield here1)
                                    (waddle l)
                                    (yield '()) ; why is this part needed???
                                    )))]
           [get-next (lambda ()
                       (call/cc (lambda ( here3 )
                                   (set! yield here3)
                                   (next 'dummy))))]
           [T? (lambda (a)
                 (let ([n (get-next)])  (display (list "next:" n))
                   (and (atom? n)
                        (or (eq? a n)
                            (T? n)))))])
    (lambda (l)
      (let ([a (get-first l)])
        (and (begin                     (display (list "first:" a))
                    (atom? a))
             (T? a))))))

我们可以看到区别:

(two-in-a-row* '(((7) (b)) c (d)))
  ; w/out yield () : (first: 7)(next: b)(next: c)(next: d)(first: ())#f
  ; w/    yield () : (first: 7)(next: b)(next: c)(next: d)(next: ())#f
  ; w/    yield #f : (first: 7)(next: b)(next: c)(next: d)(next: #f)(next: #f)#t

(two-in-a-row* '(((7) (b)) c ()))
  ; w/out yield () : (first: 7)(next: b)(next: c)(first: ())#f
  ; w/    yield () : (first: 7)(next: b)(next: c)(next: ())#f
  ; w/    yield #f : (first: 7)(next: b)(next: c)(next: #f)(next: #f)#t

(two-in-a-row* '(((7) (b)) b ()))
  ; w/out yield () : (first: 7)(next: b)(next: b)#t
  ; w/    yield () : (first: 7)(next: b)(next: b)#t
  ; w/    yield #f : (first: 7)(next: b)(next: b)#t

答案 2 :(得分:0)

这很棘手。书中的线索是wow!的答复。学生说wow!是因为他们意识到()是从其他函数返回的。

这不是很清楚,无论是在书中还是在使用drracket时,都花了我一段时间来理解,但是了解这一点的关键是:

  1. get-first称为waddle以使fill延续。
  2. waddle(不使用延续时)将返回get-first

但是

  1. get-next呼叫fill
  2. fill继续在waddle
  3. waddle使用leave返回get-next而不是get-first

但是对于(waddle '())waddle不使用leave返回get-next。它正常返回。这意味着它返回到get-first

这意味着get-next实际上不会获得()返回值。它不会获得此值,因为waddle会返回get-first

现在进入有趣的部分。

  1. 我们知道,对于值(),当我们希望waddle返回到get-first时,get-next返回到get-next
  2. 我们知道leaveget-next设置为返回get-first
  3. 因此,leave可以使用get-next返回到(leave '())

之所以棘手,真正的原因是看一下在get-first中不使用fill的情况。

  1. ()呼叫waddle蹒跚。
  2. get-first返回到get-first
  3. ()然后返回 (let ([fst '()]) ;; was (let ([fst (get-first l)]) (if (atom? fst) (T? fst) #f)))))

这等效于:

get-next

返回与返回 [T? (lambda (a) (let ([n '()]) ;; was (let ([n (get-next 'dummy)]) (if (atom? n) (or (eq? a n) (T? n)) #f)))]) 的版本相同的值:

#f

两个都是data =[ { _id: "5ba241b4efa8da2f1464ca81", title: "Zero To One", author: "Peter Thiel", isbn: 1279031943, __v: 0 } ] ,但只是偶然!没有人说这本书不会让你思考;)