Продолжения понимания справки в Схеме

Я работал вместе с Маленьким Интриганом для изучения Схемы и использовал PLT-схему своей среды.

Маленький Интриган помог мне чрезвычайно с рекурсией (это просто для меня теперь), но я застреваю на части книги, которая представляет "коллекторы" и вызывает функцию в целом продолжение.

Вот пример кода, который они использовали. Я понимаю рекурсивные элементы, но я застреваю, в особенности на функциях лямбды - мой ум не может следовать за путем и как аргументы в пользу той функции лямбды установлены (так как их единственный вызов должен назвать их снова в рекурсии, нет никакого конкретного использования в теле функции).

Если кто-то мог бы более или менее дать мне передохнуть вниз пути вычисления через рекурсию функции в коллекторы лямбды, которые могут помочь мне.

;; Build a nested list of even numbers by removing the odd ones from its
;; argument and simultaneously multiply the even numbers and sum the odd
;; numbers that occur in its argument.
(define (even-only-collector l col)
  (cond
    ((null? l)
      (col (quote ()) 1 0))
    ((atom? (car l))
      (cond
        ((even? (car l))
          (even-only-collector (cdr l)
            (lambda (newl p s)
              (col (cons (car l) newl)
                (* (car l) p) s))))
         (else
           (even-only-collector (cdr l)
             (lambda (newl p s)
               (col newl
                 p (+ (car l) s)))))))
    (else
      (even-only-collector (car l)
        (lambda (al ap as)
          (even-only-collector (cdr l)
            (lambda (dl dp ds)
              (col (cons al dl)
                (* ap dp)
                (+ as ds)))))))))

;; The collector function
(define (collector newl product sum)
  (cons sum
    (cons product newl)))

Заранее спасибо!!

43
задан Joshua Taylor 16 August 2014 в 02:49
поделиться

2 ответа

Попробуйте что-нибудь попроще, чтобы увидеть, как это работает. Например, вот версия функции list-sum, которая получает аргумент продолжения (который часто называют k):

(define (list-sum l k)
  (if (null? l)
    ???
    (list-sum (cdr l) ???)))

Основной паттерн есть, а недостающие части - это то место, где происходят интересные вещи. Аргумент продолжения - это функция, которая ожидает получить результат -- так что если список равен нулю, то понятно, что мы должны послать его 0, так как это сумма:

(define (list-sum l k)
  (if (null? l)
    (k 0)
    (list-sum (cdr l) ???)))

Теперь, когда список не равен нулю, мы вызываем функцию рекурсивно с хвостом списка (другими словами, это итерация), но вопрос в том, что должно быть продолжением. Делать это:

(define (list-sum l k)
  (if (null? l)
    (k 0)
    (list-sum (cdr l) k)))

явно неправильно -- это означает, что k в конечном итоге получит сумму (cdr l) вместо всей l. Вместо этого используйте там новую функцию, которая тоже просуммирует первый элемент l вместе со значением, которое он получит:

(define (list-sum l k)
  (if (null? l)
    (k 0)
    (list-sum (cdr l) (lambda (sum) (+ (car l) sum)))))

Это приближается, но все равно неправильно. Но стоит подумать о том, как все работает - мы вызываем list-sum с продолжением, которое само по себе получит общую сумму, и добавляем к ней первый элемент, который мы видим сейчас. Недостающая часть видна в том, что мы игнорируем k. Что нам нужно, так это составить с этой функцией k -- так мы выполним ту же самую операцию с суммой, а затем отправим результат на k:

(define (list-sum l k)
  (if (null? l)
    (k 0)
    (list-sum (cdr l) (compose k (lambda (s) (+ s (car l)))))))

, который, в конце концов, работает. (Кстати, помните, что каждая из этих функций лямбда имеет свою собственную "копию" l). Вы можете попробовать это с:

(list-sum '(1 2 3 4) (lambda (x) x))

И, наконец, заметьте, что это то же самое, что и:

(define (list-sum l k)
  (if (null? l)
    (k 0)
    (list-sum (cdr l) (lambda (s) (k (+ s (car l)))))))

если вы сделаете композицию явной.

(Вы также можете использовать этот код на промежуточном+лямбда-студентском языке, и нажать на шаговую кнопку, чтобы посмотреть, как будет происходить оценка - это займет некоторое время, но вы увидите, как вложены функции продолжения, каждая из которых имеет свой вид списка)

.
43
ответ дан 26 November 2019 в 23:07
поделиться

Вот один способ помочь вам "получить более конкретную идею". Представьте, если бы коллектор был определен таким образом:

(define (collector l p s)
  (display l)
  (newline)
  (display p)
  (newline)
  (display s)
  (newline))

В базовом случае, если Вы передаете в пустом списке, то он вызовет Вашу функцию с аргументами '(), 1, и 0. Теперь работайте с одноэлементным списком, и посмотрите, чем он вызовет Вашу функцию. Продолжайте работать с длинными и длинными списками, пока не поймете, что происходит.

Удачи!

.
1
ответ дан 26 November 2019 в 23:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: