LISP Scheme

このエントリーをはてなブックマークに追加
226デフォルトの名無しさん
call/cc (call-with-current-continuation) の使い方その4
for文
(for init test incr body ...)

test body はwhileの時と同じ。initとincrが増えただけ。
initとincrにはfor内部で参照するカウンタ変数の初期化式と増減式を記述します。
initで(i 0) とすれば、iがカウンタ変数(初期値0)として使用されます。
initで宣言したカウンタ変数はtest/incr/body内で参照できます。
227226:2001/05/23(水) 20:15
(続き)
この例では、named-letの書式をそのまま当てはめて使用してるので、letと同じ
問題を含んでいます。(よって実際のC言語のfor文より使い勝手は落ちます。)
initとincrに与える書式は、
init が () の時、 incr は () (==whileと同じ意味)
init が (x 0) の時、 incr は (+ x 1)
init が ((x 0) (y 1) (z 2)) の時 incr は ((+ x 1) y (+ z y))
を許します。
let束縛順序の規則が適用されるので、initに((x 0) (y x) (z y)) の様な式は書けません。
(被演算子側の x, yは外部に存在する物と解釈されます。)

これらの書式の判定と選択はマクロ解釈コード内で行ないます。
出力の展開コードには含まれません。
for-expand内部定義の場合分けを増やす事によって、柔軟な構文を許す事ができます。
228226:2001/05/23(水) 20:15
;実装
(define-macro (for init test incr . body)
 (let ((loop (gensym))
    (expand-init
     (if (or (null? init)
         (pair? (car init)))
       init
       (list init)) )
    (expand-incr
     (if (or (null? init)
         (pair? (car init)))
       incr
       (list incr)) ))

  `(call/cc (lambda (break)
    (let ,loop ,expand-init
     (cond
      (,test
      (call/cc (lambda (continue)
       ,@body
      )) ; call/cc continue
      (,loop ,@expand-incr)))
    ))) ; call/cc break
 ))
229226:2001/05/23(水) 20:16
;おまけ
;letが大袈裟だと思ったときに使う。
(define-macro (tmp init . body)
 (let ((expand-init
     (if (or (null? init)
         (pair? (car init)))
       init
       (list init)) ))
  `(let ,expand-init ,@body)
 ))

;forのテスト
(define (九九 from to)
 (for (y from) (<= y to) (+ y 1)
  (for (x from) (<= x to) (+ x 1)
   (tmp (n (* x y))
    (if (< n 10) (display #\space))
    (if (< n 100) (display #\space))
    (display* n #\space)
   )
  )
  (display* eol)
 ))
230226:2001/05/23(水) 20:17
;テスト結果
(九九 1 9)
 1  2  3  4  5  6  7  8  9
 2  4  6  8 10 12 14 16 18
 3  6  9 12 15 18 21 24 27
 4  8 12 16 20 24 28 32 36
 5 10 15 20 25 30 35 40 45
 6 12 18 24 30 36 42 48 54
 7 14 21 28 35 42 49 56 63
 8 16 24 32 40 48 56 64 72
 9 18 27 36 45 54 63 72 81
=>()
231226:2001/05/23(水) 20:18
ずれちゃった・・
補足:
マクロの展開処理は普通のlispの記号処理と同程度のコストが掛かるので、
記述が長くなると不利ですが、コンパイラや直コード埋め込み、マクロキャッシュ
などを持っている処理系ならば、実行時に展開を行なう回数は0〜1回に抑える
事ができます。
(逆を言えば、これらの機能の無い処理系上ではマクロを積極的に使うことは考えられない。)