Сделайте мой цикл while, используя "define-syntax-rule"
Я пытаюсь создать свой собственный цикл while в ракетке, используя "define-syntax-rule". Я хочу, чтобы он основывался на процедурном подходе, поэтому никаких вспомогательных функций (то есть просто использование лямбды, let, letrec и т. Д.)
У меня есть это, но это дает мне некоторую ошибку лямбда-идентификатора.
(define-syntax-rule (while condition body)
(lambda (iterate)
(lambda (condition body) ( (if condition)
body
iterate))))
Я хочу, чтобы это было так, чтобы я мог использовать его как обычный цикл while Например:
(while (x < 10) (+ x 1))
Вызов этого будет (должен) возвращать 10 после завершения цикла.
Как можно исправить мой код, чтобы сделать это?
2 ответа
Здесь while
из моей стандартной прелюдии вместе с примером ее использования:
Petite Chez Scheme Version 8.4
Copyright (c) 1985-2011 Cadence Research Systems
> (define-syntax while
(syntax-rules ()
((while pred? body ...)
(do () ((not pred?)) body ...))))
> (let ((x 4))
(while (< x 10)
(set! x (+ x 1)))
x)
10
Вам, вероятно, следует поговорить со своим инструктором о своих заблуждениях относительно Схемы.
Я не мог решить, было ли это домашнее задание или нет. Итак, вот версия, которая решает эту проблему: если это домашнее задание, то, вероятно, это будет очевидно для любого, кто его установит, а если это не так, интересно, как это работает.
Один вопрос, на который нужно ответить: (while #f ...)
следует оценить: это отвечает, что, сказав, что следует оценить к любому (void)
возвращается. Это также использует (begin form ...)
для секвенирования: серьезно извращенный будет использовать ((λ () form ...))
вместо конечно.
(define-syntax-rule (while condition form ...)
((λ (c) (c c (void)))
(λ (c v)
(if condition
(c c (begin form ...))
v))))
Разработка
Приведенный выше оригинальный ответ был намеренно запутан, поскольку я не хотел давать домашнее задание. Вот его безоблачная версия и ряд запутывающих / очищающих стадий, которые дают вышеупомянутое.
Прежде всего, что я хочу (while condition form ...)
сделать, это оценить формы в его теле, пока condition
не соответствует действительности, а затем возвращает значение последней формы. Если condition
никогда не верна, возврат (void)
,
Таким образом, способ сделать это с помощью цикла, и очень естественный способ выразить цикл в Scheme с помощью named- let
, Оставшаяся хитрость заключается в том, что возвращаемое значение связано в именованном let
с начальной привязкой (void)
и последующие, являющиеся результатом последней формы, через (begin form ...)
, Итак, мы получаем это:
(define-syntax-rule (while condition form ...)
(let loop ([v (void)])
(if condition
(loop (begin form ...))
v)))
И вы можете ясно видеть, что это делает то, что нужно.
Итак, первое запутывание заключается в том, что нам не нужно использовать named- let
: мы можем использовать letrec
вместо:
(define-syntax-rule (while condition form ...)
(letrec ([loop (λ (v)
(if condition
(loop (begin form ...))
v))])
(loop (void))))
Ну, это довольно ясно делает то же самое: letrec
устанавливает привязки, которые видны для привязываемого значения, так что это будет работать нормально.
Но я ненавижу letrec
: мы можем сделать это, не используя это? Да, мы можем: мы можем передать вещь для вызова самой функции:
(define-syntax-rule (while condition form ...)
(let ([loop (λ (c v)
(if condition
(c c (begin form ...))
v))])
(loop loop (void))))
Так что теперь функция не знает, что она вызывает сама себя: ей просто дана функция для вызова, которая оказывается самой собой.
Последний шаг - осознать, что (let ((x y)) ...)
это так же, как ((λ (x) ...) y)
: let
это просто немного синтаксического сахара над λ
, Поэтому мы переписываем предыдущую версию, чтобы получить это:
(define-syntax-rule (while condition form ...)
((λ (loop) (loop loop (void)))
(λ (c v)
(if condition
(c c (begin form ...))
v))))
И это, вплоть до переименования некоторых переменных, является оригинальной версией.