Simple Scheme で電卓をつくってみる(6)

Simple Schemeで電卓を作っています。
足し算と引き算を実行するコードができたので電卓に組み込みましょう。

演算ボタンが押されると何らかの関数が呼び出されるわけですが、その関数は下記のmake-opで生成することにします。

(define op add)
(define (make-op func)
  (lambda ()
    (begin (and (symbol=? mode inputting)
                (begin (set! result (op result input)) (set! window result)))
           (set! op func)
           (set! mode waiting))))

電卓で演算ボタンが押されると、③直前に入力された数と、②その前に押された演算ボタンと、①その前までの計算結果の3つに対して①②③という演算*1が実行され、結果が画面に表示されます。
演算が実行されるのは(当然ですが)当該の演算ボタンが押されたときではありません。次に数字が入力されてそのあと演算ボタン*2が押されたときです。

というわけで演算ボタンが押されたとき、opにその演算を記憶させておいてあとから取り出せるようにしておきましょう。
演算結果はresultに保存されます。

opresultの初期値はそれぞれaddと0((make-num " " '(0) '()))です*3

この電卓は内部で2つのモードのいずれかを持っています。それぞれwaitingモードとinputtingモードと呼ぶことにしました。演算ボタンが押されてから数字ボタンが押されるまでがwaitingモードで数字ボタンが押されてから演算ボタンが押されるまでがinputtingモードです。
waitingモードとinputtingモードでの各ボタンが押されたときの挙動は次の表のようになります。

モード 数字ボタン 小数点ボタン 演算ボタン
waiting inputを0にリセットしてから入力された数字を追加する inputを0にリセットしてから小数部分の入力に切り替える*4 opを最新のものに変更する
inputting inputに入力された数字を追加する 小数部分の入力に切り替える 演算を行ってからopを最新のものに変更する

つまり演算ボタンが押されると数の入力は一旦終わりで、次に数字ボタンや小数点ボタンが押されたときは新たな数の入力が始まったと見なされるわけです。

(waitingモード中の演算ボタンの入力は直前の演算ボタンの入力の取り消しです((その前に行われた)演算の取り消しではないことに注意))

数字や小数点の入力関数に、waitingモードのときの処理を追加しました。

(define waiting 'waiting) (define inputting 'inputting) (define mode waiting)

(define (rcons xs x) (append xs (list x)))
(define which-part 'int)
(define (input-number n)
  (lambda ()
    (begin
      (and (symbol=? mode waiting)
           (begin (set! mode inputting) (set! which-part 'int)
                  (set! input zero)))
      (and (< (+ (length (num-i input)) (length (num-f input))) max-ndigits)
           (begin
             (if (symbol=? which-part 'int)
                 (if (and (null? (cdr (num-i input))) (= 0 (car (num-i input))))
                     (set! input (make-num " " (list n) (num-f input)))
                     (set! input
                       (make-num " " (rcons (num-i input) n) (num-f input))))
                 (set! input (make-num " " (num-i input) (rcons (num-f input) n))))
             (set! window input))))))

(define (input-point)
  (begin (and (symbol=? mode waiting)
              (begin (set! mode inputting) (set! input zero) (set! window input)))
         (set! which-part 'frac)))

動かしてみるとこんな感じです。

続きます。

(ここまでのソースコード)

*1:例えば①が5、②が-、③が3なら5-3という演算。

*2:か=か%かM+かM-。まだどれも動きませんが。×と÷も。

*3:別にmul(掛け算)と1((make-num " " '(1) '()))でもよいのですが、掛け算はまだないので。

*4:which-partを'fracにする。