Я собрал следующий простейший секундомер в Racket (только сейчас изучаю, конечная цель — таймер-помодоро).
#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 '())
))
Хотя это делает именно то, что должно, мне было интересно, как я могу избежать изменяемого состояния. Если я следую протоколу HTDP, это та ситуация, когда изменяемое состояние оправдано, но после просмотра Уодлера "Монады для функционального программирования", мне все еще любопытно, как бы я мог обойтись без set!
.
Я знаю, что для того, чтобы это работало, я должен добавить аргументы к своим функциям. Например, start
станет
(define (start [now (current-seconds)])
now)
и аналогичный подход может работать с lap
и 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)])))