4

我正在尝试在 DrRacket 中为 while 循环创建一个宏。这是我写的:

(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。似乎当条件不成立时,系统会尝试将 void 作为参数应用到某个过程。

4

4 回答 4

8

哎哟:

  1. 使用这种while循环风格会鼓励过度使用命令式编程。
  2. 使用define-macro会创建不卫生的宏,这在 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)).

于 2012-06-10T14:15:03.683 回答
2

while 的另一个版本使用 do 循环:

(define-syntax while
  (syntax-rules ()
    ((while pred? stmt ...)
      (do () ((not pred?))
        stmt ...))))
于 2012-06-10T14:27:25.760 回答
2

当一个普通的旧函数可以使用时,为什么要使用宏?

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

当然,这需要您传入可以调用的可重复操作(这就是为什么conditionbody在 的主体中被括号包围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))

你会注意到前两个参数现在是函数 from Stateto something,并且应用到 2 个参数的结果也是一个函数 from State -> State。这是一个反复出现的模式,作为 Haskeller,我称之为“状态单子”。不过,将糖放在这个概念之上的讨论有点超出了这个对话的范围,所以我就停在那里。

于 2012-06-11T21:29:59.013 回答
1

因为已经有一段时间了:

whileRacket 6.0的宏

#lang racket

(define-syntax while
  (syntax-rules ()
    ((_ pred? stmt ...)
     (do () ((not pred?))
      stmt ...))))
于 2014-05-25T17:18:17.513 回答