在DrRacket中循环宏

时间:2012-06-10 11:14:08

标签: macros scheme racket

我正在尝试在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 个答案:

答案 0 :(得分:7)

哎哟:

  1. 使用这种while循环样式会鼓励过度使用命令式编程。
  2. 使用define-macro创建不卫生的宏,这是Scheme中的噩梦。
  3. 虽然我不鼓励写一个命令式的循环宏,但供你参考,这里是同一个宏的非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))

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

为什么在使用普通旧函数时会使用宏?

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

当然,这需要您传递可以调用的可重复操作(这就是conditionbodyfun-while体中的parens包围的原因,所以你如果你想要更漂亮的语法,确实需要一个宏。但是一旦你有一个具有所需行为的函数,在这种情况下将一些糖放在上面是微不足道的:

(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的函数。这是一种反复出现的模式,作为Haskeller,我称之为“State Monad”。然而,关于将糖置于这个概念之上的讨论有点超出了这次谈话的范围,所以我将停在那里。

答案 3 :(得分:0)

因为已经有一段时间了:

针对Racket 6.0的while

#lang racket

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