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

さらに心理的に落ち着くために、これからしようとしている計算が足し算なのか引き算なのか掛け算なのか割り算なのかを表示するようにしましょう。

そのためには、これからしようとしている計算を表す記号を電卓の表示機構に知らせる必要があります。
まず演算記号のリストと関数のリストを作りましょう。このとき、対応する記号と関数が同じ添字の位置に来るようにします。*1

(define op-no 0) (define op-labels (list "" "+" "-"))

; ...

(define op-alist (list (list "" get2nd) (list "+" add) (list "-" sub)))
(define ops (map (lambda (l) (cadr (assoc l op-alist))) op-labels))

そして、make-opに直接add等を渡すのをやめて、番号(op-no)を渡すことにします。

(define (make-op i)
  (lambda ()
    (begin (and (symbol=? mode inputting)
                (begin (set! result ((list-ref ops op-no) result input)) ; add等へは添字でアクセスする
                       (set! window result)))
           (set! op-no i) (set! mode waiting))))

この番号は主にop-labelsopsにアクセスするための添字として、また演算記号の表示位置を決めるための値としても使用します。

(define overlay-window
  (let* ((wdx (* width (/ 3/4 (+ max-ndigits 1/2)))) (iwdx (round wdx))
         (wy (* width 9/64)) (sy (* width 5/64))
         (sconv-x (lambda (x) (round (* width (- 9/10 (* 1/20 x)))))))
    (lambda (scn)
      (let* ((lis (map number->string (append (num-i window) (num-f window))))
             (wconv-x (lambda (x)
                        (round (+ (* width 1/8)
                                  (* wdx (+ x (- max-ndigits (length lis)) 1))))))
             (scn (place-image (text (list-ref op-labels op-no) 60 "#888")  ; ここと
                               (sconv-x op-no) sy scn)))  ; ここ
        (foldl (lambda (x scn)
                 (place-image (text (car x) iwdx "black") (cadr x) wy scn))
               (place-image (text "." iwdx "black")
                            (wconv-x (- (length (num-i window)) 0.5)) wy
                            (place-image (text (num-sign window) iwdx "black")
                                         (wconv-x -0.8) wy scn))
               (build-list (length lis)
                           (lambda (i) (list (list-ref lis i) (wconv-x i)))))))))

実際に動かしてみたのがこちらです*2

他にもいろいろ変えたので列挙しておきますね。

続きます。

*1:今あるのは加減算だけなのでリストも加減算(とget2nd)だけです。

*2:-の位置が低いですが、ボタンのM-も小さいし、この手の形状のずれはあまり気にしないことにします(そのうち全角にする可能性もなくはないです)。

*3:filterを使っているので余分な探索が入りますが、余分を省くために自分でループを書くよりは多分速いです。