2

请看two-in-a-row*?第 19 章中的函数。

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

没有(leave '())它仍然会返回这两种值,只是不使用 continuation 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)))))

非常感谢。

关于这个问题的另一个有趣的步骤。

4

3 回答 3

3

所以感谢 Will Ness 的例子。我浏览了一些更简单的。所以“这有什么不好?” - 没有(leave '())get-first.

简短回答:
请注意,从我的代码中,
i)leave将始终在我们每次调用get-first或时重新创建get-next。它将返回到get-firstget-next
ii)fill将根据先前的 导致成为一个链fill,并且它将始终返回到get-first

示例
输入:'(1)

所以我们从评估get-firston开始'(1)
i) 设置leave
ii) 开始(waddle '(1)
iii) 因为1是原子,所以设置fill为当前延续。注意如果我们使用fill,那么它会去做(waddle (cdr l)),然后它会返回get-first。iv)get-first通过使用leave返回值返回1

然后我们去 eval (T? 1),它又会去 run get-next
i) 设置leave
ii) 运行fill
iii) 开始(waddle '())
iv)()从返回waddle,然后返回get-first


1)如果我们没有(leave '(),那么get-first将返回'(),然后two-in-a-row*返回#f。所以我们可以得到相同的答案,但行为不是我们想要的。
2)如果我们有它,那么请注意leave现在是leave由 创建的get-next,因此它将转移'()get-next
3)当我们创建时列表中的输入超过 1 个,fill它将基于 previous 创建fill,因此结果是一个依赖于 previous 的链fill

于 2017-02-07T21:40:32.243 回答
2

命名看起来不对劲。我用“yield”表示“leave”,用“next”表示“fill”。我还必须定义atom?并重写letcccall/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
于 2017-02-07T01:50:13.837 回答
2

这很棘手。书中的线索就是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-firstget-next
  2. 我们知道get-next集合leave要返回get-next
  3. 因此, get-first可以使用leave返回get-next

这很棘手的真正原因是,当您不使用(leave '())in时,请查看场景get-first

  1. fill().
  2. waddle返回get-first.
  3. get-first然后返回()

这相当于:

  (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,但只是偶然!没有人说这本书不会让你思考;)

于 2018-09-26T14:31:59.930 回答