【新卒研修】pythonで作る電卓

# はじめに
python研修の第一歩として電卓のプログラミングをしました。今回の記事では、連続して四則演算ができるシンプルな電卓の作成をします。

# 期待結果
数値と演算子(+, -, *, /, =)を交互に入力して、最後に=を入れると今まで入力された計算の結果が表示されます。
また、入力ミスに対してはエラーメッセージが出力される仕様です。

~~~
入力？->a
エラー：１つ目は数値を入力してください。
入力？->1
入力？->+
入力？->
入力？->a
エラー：記号（+,-,*,/,=）のあとは必ず数値を入力してください。
入力？->=
エラー：記号（+,-,*,/,=）のあとは必ず数値を入力してください。
入力？->10
入力？->*
入力？->3
入力？->=
計算結果：1.0+10.0=11.0
計算結果：11.0*3.0=33.0
答え：33.0
~~~

# 関数の定義
値sが整数や小数かどうかを判定する関数です。
~~~
# 数値かどうかを判定する関数
def number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False
~~~

引数sをfloatで浮動小数点数に変換できればそれは数値として扱えるため、変換に成功すればTrueを返します。floatの変換ができない(abcや+の)場合はFalseを返します。

#### 実行例
~~~
number("123")      # → True
number("3.14")     # → True
number("abc")      # → False
number("+")        # → False
number("")         # → False
number("0")        # → True
~~~
# 状態管理
この電卓では、数値と記号を交互に入力する必要があります。これを実現するために、以下のように記述しています。
~~~
expect_number = True 
~~~
この変数は、入力が数値か記号のどちらを受け付ける状態かを管理するために使います。
Trueのとき次は数値を期待し、Falseのとき次は記号を期待します。


~~~
# 数値を入力した場合
history.append(float(value))
expect_number = False  # 次は記号を期待

# 記号を入力した場合
history.append(value)
expect_number = True  # 次は数値を期待
~~~

このようにすることで、誤った入力(+のあとにまた+など)を防ぐことができます。


# 計算処理
=が入力されたとき、historyに格納された数値と記号を元に計算を行います。
~~~
result = history[0] 

for i in range(1, len(history) - 1, 2):  # 記号のインデックスを走査
    sign = history[i]
    next_num = history[i + 1]
 ~~~
最初の数値をresultの初期値とします。
forループで、演算子と次の数値の組み合わせを1回ずつ順番に計算しています。
history(履歴)には [数値, 記号, 数値,  ..., 記号, 数値] のように格納され、rangeによって奇数番目(記号)を順に参照します。
そしてその記号と、その次の数値を取り出して、演算に使用します。

# ソースコード

~~~~
signs = ["+", "-", "*", "/", "="]

# 数値かどうかを判定する関数
def number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

history = []  # 入力の履歴
expect_number = True  # 次に数値を期待しているかどうか

while True:
    value = input("入力？->")

    if not value:
        continue  # 空入力は無視

    # 数値を期待しているとき
    if expect_number:
        if number(value):
            history.append(float(value))  # 数値として履歴に追加
            expect_number = False  # 次は記号を期待
        else:
            if not history:
                print("エラー：１つ目は数値を入力してください。")
            elif isinstance(history[-1], str) and history[-1] in signs:
                print("エラー：記号（+,-,*,/,=）のあとは必ず数値を入力してください。")
            else:
                print("エラー：数値を入力してください。")

    # 記号を期待しているとき
    else:
        if value in signs:
            if value == "=":
                if isinstance(history[-1], str):
                    print("エラー：記号のあとは数値を入力してください。")
                    continue

                # 計算処理
                result = history[0]
                for i in range(1, len(history) - 1, 2):
                    sign = history[i]
                    next_num = history[i + 1]
                    prev_result = result

                    if sign == "+":
                        result += next_num
                    elif sign == "-":
                        result -= next_num
                    elif sign == "*":
                        result *= next_num
                    elif sign == "/":
                        if next_num == 0:
                            print("エラー：0で割ることはできません。")
                            break
                        result /= next_num

                    print(f"計算結果：{prev_result}{sign}{next_num}={result}")

                print(f"答え：{result}")
                break  # 計算終了
            else:
                history.append(value)
                expect_number = True
        else:
            print("エラー：数値または記号（+,-,*,/,=）を入力してください。")
~~~~

全体のソースコードは以上のようになります。
いつかGUIなどもつけていきたいですね。