Сделайте мой цикл 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))))

И это, вплоть до переименования некоторых переменных, является оригинальной версией.

Другие вопросы по тегам