Avoid volatile state when I need to store (current seconds) several times

I put together the next rudimentary stopwatch in Racket (just studying now, the final goal is a pomomoro timer).

#lang racket

(define start-time 0)
(define end-times '())

(define (start);; stores start-time
  (set! start-time (current-seconds)))

(define (lap);; stores "laps" in list
  (set! end-times (cons (current-seconds) end-times)))

(define (stop);; stores final time, displays lap-times in h, m, s and resets end-times
  (begin
    (set! end-times (cons (current-seconds) end-times))
    (display
     (reverse
      (map (lambda (an-end)
             (let ((the-date (seconds->date(- an-end start-time))))
               (list
                (sub1(date-hour the-date))
                ;; sub1 is needed because (date-hour(seconds->date 0) = 1
                (date-minute the-date)
                (date-second the-date)))) end-times)))
    (set! end-times '())
    ))

While this does exactly what I need, I was wondering how I can avoid a volatile state. If I follow HTDP, it is such a situation where the mutable state is justified, but after watching Wadler's " Monads for Functional Programming ", I am still curious how I could do without set!.

I know that for its functionality I have to add arguments to my functions. For example, startwill

(define (start [now (current-seconds)])
  now)

and a similar approach can work with lapand stop.

, , , , , , set!.

: (!), . . - @Metaxal @Greg Hendershott.

#lang racket

(define (run)
  (displayln "Enter 'lap' or 'quit':")
  (let loop ([t0 (current-seconds)] [times '()])
    (match (read-line)
      ["quit" (reverse
      (map (lambda (x)
             (let ((the-date (seconds->date x)))
               (list
                (sub1(date-hour the-date))
                (date-minute the-date)
                (date-second the-date)))) times))]
      ["lap" (loop t0 (cons (- (current-seconds) t0) times))]
      [_ (loop t0 times)])))
+4
3

, , . , , , ( ).

:

(define (loop [t0 (current-seconds)] [times '()])
  ;; ... do things here, possibly depending on user input ...
  ;; then loop with a new state:
  (cond [<some-start-condition> (loop (current-seconds) '())]
        [<some-lap-condition>   (loop t0 (cons (- (current-seconds) t0) times))]
        [<some-stop-condition>  times])) ; stop, no loop, return value

, , .

, ( ) . Racket (, ) big-bang, .

+2

set! , "" . , , , , . , - , lambda!

(define (make-timer)

  ; the "attributes" of the object

  (let ([start-time  0]
        [end-times '()])

    ; the "methods" of the object

    (define (start)
      (set! start-time (current-seconds)))

    (define (lap)
      (set! end-times (append end-times (list (current-seconds)))))

    (define (stop)
      (lap)
      (display
       (map (lambda (an-end)
              (let ((the-date (seconds->date (- an-end start-time))))
                (list
                 (sub1 (date-hour the-date))
                 (date-minute the-date)
                 (date-second the-date))))
            end-times))
      (set! end-times '()))

    ; return a dispatch procedure

    (lambda (msg)
      (case msg
        ((start) (start)) ; call the start procedure defined above
        ((lap)   (lap))   ; call the lap procedure defined above
        ((stop)  (stop))  ; call the stop procedure defined above
        (else (error "unknown message:" msg))))))

, . , :

(define timer (make-timer))

(timer 'start)
(sleep 1)
(timer 'lap)
(sleep 1)
(timer 'lap)
(sleep 1)
(timer 'lap)
(sleep 1)
(timer 'stop)

=> ((18 0 1) (18 0 2) (18 0 3) (18 0 4))

" ", SICP.

+1

, , , , , @Metaxal .

, struct:

(struct state (start-time end-times))

start, lap stop state:

;; start : -> state
;; stores start-time
(define (start)
  (state (current-seconds) '()))

;; lap : state -> state
;; stores "laps" in list
(define (lap st)
  (match-define (state start-time end-times) st)
  (state start-time
         (cons (current-seconds) end-times)))

;; stop : state -> list
;; stores final time, displays lap-times in h, m, s
(define (stop st)
  (match-define (state start-time end-times*) st)
  (define end-times (cons (current-seconds) end-times*))
  (reverse
   (map (lambda (an-end)
          (let ((the-date (seconds->date(- an-end start-time))))
            (list
             (sub1(date-hour the-date))
             ;; sub1 is needed because (date-hour(seconds->date 0) = 1
             (date-minute the-date)
             (date-second the-date)))) end-times)))

@Metaxal, " " "" :

:

(define (run)
  (displayln "Enter 'lap' or 'quit':")
  (let loop ([st (start)])
    (match (read-line)
      ["quit" (stop st)]
      ["lap" (loop (lap st))]
      [_ (loop st)])))

@Óscar López , SICP.

Racket ( Scheme) , , , , , - , , .

+1

Source: https://habr.com/ru/post/1537562/


All Articles