Macros DrRacket中的While循环宏

Macros DrRacket中的While循环宏,macros,scheme,racket,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

我正在尝试为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))))
(需要mzlib/defmacro)
(定义我的宏)
(lambda(状态体)
(列表“本地”(列表“定义(列表)”while循环)
(列出“如果条件”
(列表主体(列表的while循环))
"(无效))
“(while循环)))
(定义x 0)
(我的时间(
该程序的输出为:

0
1
2
3
4
5
6
7
8
9
error:  procedure application: expected procedure, given: #<void>; arguments were: #<void>
0
1.
2.
3.
4.
5.
6.
7.
8.
9
错误:过程应用程序:预期过程,给定:#;论点如下:#
有人能帮我吗?为什么这个宏不能终止并返回void呢。似乎当条件为非真时,系统会尝试将void作为参数应用于某些过程

哎哟

  • 使用这种风格的
    while
    循环会鼓励过度使用命令式编程
  • 使用
    define macro
    创建不卫生的宏,这在Scheme中是一场噩梦
  • 虽然我不鼓励编写命令式循环宏,但这里有一个相同宏的非
    define宏版本供您参考:

    (define-syntax-rule (my-while condition body ...)
      (let loop ()
        (when condition
          body ...
          (loop))))
    
    它使用
    语法规则
    ,可以创建卫生的宏,并且比现有的要容易得多


    现在,对于您问题的实际答案,首先,让我们以更可读的方式写出原始宏:

    (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))
    ,另一个版本的while使用do循环:

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

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

    ;; fun-while : (-> Boolean) (-> Any) -> Void
    (define (fun-while condition body)
      (when (condition)
        (body)
        (fun-while condition body))
    
    当然,这需要您传入可调用的可重复操作(这就是为什么
    条件
    主体
    乐趣的主体中被paren包围的原因,而
    乐趣的主体
    ),因此如果您想要更漂亮的语法,确实需要一个宏。但是,一旦你有了一个具有所需行为的函数,在上面放一些糖对于这种情况来说是微不足道的:

    (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
    到某物的函数,应用于两个参数的结果也是从
    State->State
    到某物的函数。这是一个反复出现的模式,作为Haskell,我将其称为“状态蒙纳德”。关于将糖放在这个概念之上的讨论超出了本次对话的范围,所以我就到此为止。

    因为已经有一段时间了:

    球拍6.0的
    while

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

    真的,真的。但我今天才第一次看到一个宏(在Fixnum天的自学计划中)。还在学习。我还没有读过关于卫生宏的书。@RajeshBhat:另外,我刚刚更新了我的答案,解释了为什么你的不卫生宏不起作用。:-)当然,我仍然鼓励你使用卫生版@RajeshBhat宏是一项世界性的研究。Fixnum天中的方案很好,但有关宏的部分已经过时。如果我是你,我会找到一个介绍宏使用语法规则或语法大小写。我可以推荐《球拍指南》中的介绍:这确实有效,尽管
    do
    循环通常会宏扩展为基于命名的
    let
    循环,而且许多策划人告诉我,命名的
    let
    循环比
    do
    循环更惯用,后者更……CL风格