В то время как Loop Macro в DrRacket

Я пытаюсь создать макрос для цикла while в DrRacket. Вот что я написал:

(require mzlib/defmacro)

(define-macro my-while
  (lambda (condition  body)
    (list 'local (list (list 'define (list 'while-loop)
                             (list 'if condition
                                   (list body (list 'while-loop))
                                   '(void))))
          '(while-loop))))


(define x 0)

(my-while (< x 10)
          (begin              
            (display x)
            (newline)
            (set! x (+ x 1))))

Выход этой программы:

0
1
2
3
4
5
6
7
8
9
error:  procedure application: expected procedure, given: #<void>; arguments were: #<void>

Может кто-то помочь мне с этим? Почему этот макрос просто не завершается и не возвращает void. Кажется, что когдаcondition неверно, система пытается применить пустоту в качестве аргумента к какой-либо процедуре.

Ответы на вопрос(4)

Другая версия while использует цикл do:

(define-syntax while
  (syntax-rules ()
    ((while pred? stmt ...)
      (do () ((not pred?))
        stmt ...))))

Потому что это было давно:

while макрос для ракетки 6.0

#lang racket

(define-syntax while
  (syntax-rules ()
    ((_ pred? stmt ...)
     (do () ((not pred?))
      stmt ...))))

Зачем использовать макрос, если подойдет старая функция?

;; fun-while : (-> Boolean) (-> Any) -> Void
(define (fun-while condition body)
  (when (condition)
    (body)
    (fun-while condition body))

Конечно, это требует от вас повторяющихся действий, которые можно вызывать (вот почемуcondition а такжеbody окружены парнями в телеfun-while), поэтому вам нужен макрос, если вы хотите более красивый синтаксис. Но как только у вас есть функция, которая имеет желаемое поведение, положить немного сахара на верх для этого случая тривиально:

(define-syntax-rule (my-while condition body ...)
   (fun-while (lambda () condition)
              (lambda () body ...)))

Теперь, как уже было сказано, это поощряет императивный стиль, который осуждается. Вместо мутации попробуйте сделать состояние явным:

;; pure-while : forall State.
;;    (State -> Boolean)   ; the "condition" that inspects the state
;;    (State -> State)     ; the "body" that moves from one state to the next
;;    ->   ; curried
;;    State                ; the current state
;; -> State                ; produces the ending state
(define ((pure-while condition make-next) current-state)
   (if (condition current-state)
       (pure-while condition make-next (make-next current-state))
       current-state))

Вы заметите, что первые два аргумента теперь являются функциями изState к чему-то, и результат применения к 2 аргументам также является функцией отState -> State, Это повторяющийся паттерн, который, как Хаскеллер, я бы назвал «Государственной монадой». Однако обсуждение вопроса о том, как положить сахар на вершину этой концепции, немного выходит за рамки этого разговора, поэтому я просто остановлюсь на этом.

Решение Вопроса

Уч:

  1. Using this style of while loop encourages excessive use of imperative programming.
  2. Using define-macro creates unhygienic macros, which is a nightmare in Scheme.

Хотя я не рекомендую писать макрос цикла императивного стиля, для справки, здесь неdefine-macro версия того же макроса:

(define-syntax-rule (my-while condition body ...)
  (let loop ()
    (when condition
      body ...
      (loop))))

Оно используетsyntax-rules, который создает гигиенические макросы, и намного, намного легче читать, чем то, что у вас есть.


Теперь для фактического ответа на ваш вопрос, во-первых, давайте напишем ваш оригинальный макрос более читабельным образом:

(define-macro my-while
  (lambda (condition body)
    `(local ((define (while-loop)
               (if ,condition
                   (,body (while-loop))
                   (void))))
       (while-loop))))

Как только вы напишите это таким образом, вы можете увидеть, где настоящая проблема: в(,body (while-loop)) линия, которая должна была быть(begin ,body (while-loop)).

Ваш ответ на вопрос