我正在尝试在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作为参数应用于某个过程。
答案 0 :(得分:7)
哎哟:
while
循环样式会鼓励过度使用命令式编程。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))
。
答案 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))
当然,这需要您传递可以调用的可重复操作(这就是condition
和body
被fun-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 ...))))