【问题标题】:get-first - the-seasoned-schemer先入为主的老谋深算者
【发布时间】:2017-06-22 01:06:57
【问题描述】:

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

我的问题是关于 get-first 辅助函数中的 (leave '())。请注意,(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

【问题讨论】:

    标签: scheme continuations callcc seasoned-schemer


    【解决方案1】:

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

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

    示例
    输入:'(1)

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

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

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

    【讨论】:

      【解决方案2】:

      命名看起来不对。我用“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
      

      【讨论】:

      • 嗨@Will Ness,太好了!!!我现在明白它是如何工作的。我会给你学分。我会在另一个答案中写下整个事情。
      • @Lingxiao 很高兴它以某种方式帮助了你。这是一个真正的心灵弯曲。 :)
      【解决方案3】:

      这很棘手。书中的线索是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. 我们知道,对于值(),当我们希望它返回到get-next 时,waddle 会返回到get-first
      2. 我们知道get-nextleave 设置为返回get-next
      3. 因此get-first可以使用leave返回get-next

      这很棘手的真正原因是,看看你在get-first 中不使用(leave '()) 的场景。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-13
        • 1970-01-01
        • 2023-02-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多