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))

もちろん、これには呼び出すことができる繰り返し可能なアクションを渡す必要があります(これが理由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))

最初の2つの引数がfromStateから何かへの関数になり、2つの引数に適用した結果も。からの関数であることがわかりますState -> State。これは繰り返し発生するパターンであり、ハスケラーとして、私は「ステートモナド」と呼んでいます。ただし、この概念の上に砂糖を置くことについての議論は、この会話の範囲を少し超えているので、ここでやめます。

于 2012-06-11T21:29:59.013 に答える
1

久しぶりです:

whileRacket6.0のマクロ

#lang racket

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