我在 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,这是一种需要可变状态的情况,但是在浏览了 Wadler 的“函数式编程的 Monads ”之后,我仍然对没有set!
.
我知道要使其正常工作,我应该为我的函数添加参数。例如,start
将成为
(define (start [now (current-seconds)])
now)
类似的方法可以与lap
and一起使用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)])))