Ответ 1
Попробуйте что-то более простое, чтобы увидеть, как это работает. Например, здесь существует версия функции 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)))))))
который, наконец, работает. (Кстати, помните, что каждая из этих функций lambda
имеет свою собственную "копию" 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)))))))
если вы сделаете состав явным.
(Вы также можете использовать этот код на промежуточном + лямбда-языке студента и нажмите кнопку шага, чтобы посмотреть, как проходит оценка - это займет некоторое время, чтобы перейти, но вы увидите, как будут выполняться функции продолжения вложенных, каждый из которых имеет собственный вид списка.)