получить первый - опытный интриган

Пожалуйста, взгляните на функцию two-in-a-row*? в главе 19.

Мой вопрос касается (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)))))

Большое спасибо.

Еще одна интересная последовательность об этой проблеме.


person Lin    schedule 04.02.2017    source источник


Ответы (3)


Так что спасибо за примеры Уилла Несса. Я пробежался по еще более простым. Так что же в этом плохого? -- без (leave '()) в get-first.

Краткий ответ:
обратите внимание, что из моего кода
i) leave всегда будет воссоздаваться каждый раз, когда мы вызываем get-first или get-next. Он вернется либо к get-first, либо к get-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, используя leave с возвращаемым значением 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 теперь является leave, созданным get-next, поэтому он собирается передать '() в get-next.< br> 3) При наличии более 1 входа в списке, когда мы создаем fill, он будет создан на основе предыдущего fill, таким образом, результатом будет цепочка, зависящая от предыдущего fill.

person Lin    schedule 07.02.2017

Именование выглядит не так. Я использовал «выход» для «оставить» и «следующий» для «заполнить». Мне также пришлось определить 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
person Will Ness    schedule 07.02.2017
comment
Привет @Will Ness, отлично!!! Теперь я понимаю, как это работает. Я дам вам кредиты для этого. Я напишу все это в другом ответе. - person Lin; 08.02.2017
comment
@Lingxiao Рад, что это тебе как-то помогло. Это настоящий умопомрачительный. :) - person Will Ness; 08.02.2017

Это сложно. Подсказкой в ​​книге является ответ 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.
  2. Мы знаем, что get-next устанавливает leave для возврата к get-next.
  3. Следовательно, get-first может использовать leave, чтобы вернуться к get-next.

Настоящая причина, по которой это сложно, заключается в том, что посмотрите на сценарий, когда вы не используете (leave '()) в 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, но только случайно! Никто не говорил, что книга не заставит задуматься ;)

person mmm111mmm    schedule 26.09.2018