結城浩のSICP日記 RSSフィード

2006-05-06

[]p.131:make-monitored p.131:make-monitored - 結城浩のSICP日記 を含むブックマーク

Schemeの勉強はしていたけれど、さっぱりSICPを読んでいなかった (^_^;

ひげぽんさんが問題3.1〜3.6のあたりをやっていたので、私も考えた。

問題3.2:関数をラップして呼び出し回数をカウントする関数make-monitoredを作る。

(define (make-monitored f)
  (define counter 0)
  (lambda (arg)
    (cond ((eq? arg 'how-many-calls?) counter)
          ((eq? arg 'reset-count) (set! counter 0))
          (else
            (set! counter (+ counter 1))
            (f arg)))))

(define s (make-monitored sqrt))

(s 'how-many-calls?)
;=> 0

(s 100)
;=> 10.0

(s 'how-many-calls?)
;=> 1

(s 256)
;=> 16.0

(s 'how-many-calls?)
;=> 2

(s 'reset-count)
;=> 0

(s 'how-many-calls?)
;=> 0

でもこれだと、make-monitoredの引数として与える関数のarityは1に決まってしまうので、たとえば + の回数を数えることはできないはず。

(define (make-monitored f)
  (define counter 0)
  (lambda (arg)
    (cond ((eq? arg 'how-many-calls?) counter)
          ((eq? arg 'reset-count) (set! counter 0))
          (else
            (set! counter (+ counter 1))
            (f arg)))))

(define add (make-monitored +))

(add 1 (add 2 3) (add 4 5 6))
;*** ERROR: wrong number of arguments for #<closure (make-monitored make-monitored)> (required 1, got 2)

なので、修正。

(define (make-monitored f)
  (define counter 0)
  (define (call-it . args)
    (set! counter (+ counter 1))
    (apply f args))
  (lambda args
    (cond ((null? args) (call-it))
          ((eq? (car args) 'how-many-calls?) counter)
          ((eq? (car args) 'reset-count) (set! counter 0))
          (else (apply call-it args)))))

(define add (make-monitored +))

(add 1 (add 2 3) (add 4 5 6))
;=> 21

(add 'how-many-calls?)
;=> 3

よしよし。できたできた。

疑問:任意個引数の関数を呼び出すのには上記のようにapplyを使うしかないのでしょうか。最初、(f . args)などと書いてたくさん怒られました。

と、ここまで書いて、ひげぽんさんの解答を読む。ひげぽんさん、問題3.2でreset-countに対応していませんよう!

higeponhigepon2006/05/06 18:54>reset-countに対応していませんよう!
わあ。本当だ。気づいていませんでした。どもです。

トラックバック - http://sicp.g.hatena.ne.jp/hyuki/20060506