简体   繁体   中英

Counter inside a procedure in Scheme

I wrote a procedure that displays a message "msg0" "freq0" times and "msg1" "freq1" times as it is called and keeps doing this periodically.

Here is my code for such a procedure:

(define (make-counter n)
  (lambda () (set! n (+ n 1)) n))

(define counter1 (make-counter -1))

(define (rotating-msg msg0 msg1 freq0 freq1)
  (let ([period (+ freq0 freq1)]
        [count (counter1)])
    (cond ((< (remainder count period) freq0) msg0)
          (else msg1))))

This works as I intended as long as I call it the following way:

> (rotating-msg 'foo 'bar 1 2)
foo
> (rotating-msg 'foo 'bar 1 2)
bar
> (rotating-msg 'foo 'bar 1 2)
bar
> (rotating-msg 'foo 'bar 1 2)
foo

However, the following does not work:

> (define foobar (rotating-msg 'foo 'bar 1 2))
> foobar
foo
> foobar
foo
> foobar
foo

In what way exactly is this one different from the previous one?

In your first example, you call counter1 every time, and this adds 1 to n. In your second example, counter1 is only called once and hence n stays at value 0.

The canonical way for this is as follows:

(define (rotating-message msg0 msg1 freq0 freq1)
  (let ((n 0) (total (+ freq0 freq1)))
    (lambda ()
      (let ((nn (modulo n total)))
        (begin0
          (if (< nn freq0) msg0 msg1)
          (set! n (+ n 1)))))))

(define r (rotating-message 'foo 'bar 1 2))

(for/list ((i (in-range 10))) (r))
=>'(foo bar bar foo bar bar foo bar bar foo)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM