4

次の基本的なストップウォッチを 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 の「関数型プログラミングのためのモナド」を閲覧した後でも、set!.

機能させるには、関数に引数を追加する必要があることを知っています。たとえばstart

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

同様のアプローチが と で機能する可能性がlapありstopます。

それでも、機能を復元するために追加の引数を追加した後、変数に値を格納するのではなく、引数も渡す必要があることはわかっていますが、この場合、これを活用して回避する方法もわかりませんset!

更新:以下の3つの回答はすべて非常に価値があるため(ありがとうございます!)、一意の正解としてマークしませんでした. 以下は、私の最初の質問に対する最小限の解決策です。これは、@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 に答える 3

1

このような単純な例については、おそらく @Metaxal が提案したことを行うでしょう。

ただし、別のアプローチとして、状態を次のように明示的に定義することもできますstruct

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

次に、startlap、および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 で説明されている OOP のスタイルを示しています。

Racket (および Scheme) の優れた点は、目前の問題と好みに最も適していると思われる範囲で、単純な命令型、OOP 命令型、純粋関数型など、どのようなアプローチでも選択できることです。

于 2014-04-22T21:35:32.520 に答える