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

数を入力できるようになりました。
入力した数は、整数部分と小数部分をそれぞれトップレベル変数で保持するようになっています。画面に表示するときはこれらの変数から直接数値を取り出してイメージに変換して表示しています。
しかし、この方法では入力した数値以外の数を表示できません。
そこで、表示するデータを保持する、windowという変数を別に作ることにしました。
数字を表示するときは、まず表示したい数をwindowへ代入することになります。
代入を簡単にするために、structureを使って、整数部分と小数部分を1つにまとめることにしました。

(define-struct num (sign i f))
(define input (make-num " " '(0) '())) (define window input)
(define result #f) (define memory input)

数を保持するstructureの型名はnumです。numには整数部分と小数部分だけでなく符号も加えました。入力内容が負数になることはありませんが、計算結果やメモリの中身が負数になることがあるからです*1

数字ボタンをタップするとinput-number内で生成された関数が起動します。この関数の最後にwindowへの代入処理を追加しました。
*2

(define (rcons xs x) (append xs (list x)))
(define which-part 'int)
(define (input-number n)
  (lambda ()
    (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)))))

表示用データはwindow経由で取り出します。

(define (overlay-window scn)
  (let* ((lis (map number->string (append (num-i window) (num-f window))))
         (wdx (* width (/ 3/4 (+ max-ndigits 1/2)))) (iwdx (round wdx))
         (wconv-x (lambda (x)
                    (round (+ (* width 1/8)
                              (* wdx (+ x (- max-ndigits (length lis)) 1))))))
     (y (/ width 8)))
    (foldl (lambda (x scn) (place-image (text (car x) iwdx "black") (cadr x) y scn))
           (place-image (text "." iwdx "black")
                        (wconv-x (- (length (num-i window)) 0.5)) y
                        (place-image (text (num-sign window) iwdx "black")
                                     (wconv-x -0.8) y scn))
           (build-list (length lis)
                       (lambda (i) (list (list-ref lis i) (wconv-x i)))))))

入力した数字の表示部分を少し広げ、数字のサイズを少し小さくしました。
また、数字の表示位置を少し右にずらしました。
これらの変更の目的は、符号を表示するための場所を空けることです。
*3

古いバージョンの数字のサイズ(wdx)と数字位置決定関数(wconv-x

枠をつけました。この場合keyboardがバインドしているのは電源オフ状態の電卓のイメージですね。じゃあkeyboardという名前をやめろってなりますけど、どうしましょう。また今度考えます。

(define keyboard
  (place-image (round-rectangle (round (* 27/34 width)) 150 5 "outline" "gray")
               (/ width 2) (/ width 7)
               (place-buttons 0 0 buttons (empty-scene))))

f:id:brv00:20191106205910p:plain:w250

続きます。

(ここまでのコード)

*1:補数表現ではなく符号を別に保持するようにしたのは画面表示が楽だからですが、ほかの処理は面倒になるかもしれません(面倒さの度合いによっては符号を別に保持するのをやめるかも)。

*2:ほかにも、下位桁が末尾に追加されるように変更しています。この順で並べるほうが多倍長計算が簡単そうなので

*3:桁の並び順が逆になったのでそこも変更しました。wconv-xを少し変えればもとの順でも表示できるので、少しの差どころか差はなかったっぽいです。