JavaScriptで電卓を作る3
こんにちは、tushiko23です 👋
今回はついに 計算処理の仕上げ をやっていきます!
例として、次の計算で考えます👇
3 - 1 + 2 × -4 ÷ 2 = -2
前回は、display に連結された文字列を分解し、
- を 符号(単項マイナス) と 演算子(引き算) に判定して、次の配列にできるところまで進みました。
tokens = ['3','-','1','+','2','*','-4','/','2']
今日はここから、
-
掛け算・割り算(* /)を優先して計算する
-
= 押下時に 足し算・引き算(+ -)を処理して結果表示する
この2つを加えて、電卓を完成させます!🎉
それでは順番に見ていきましょう!
① 掛け算・割り算の優先順位を処理する ✖️➗
四則演算は * / が + - より優先 されます。
なので、先に * / を処理し、配列を「足し算・引き算だけ」に整えます。
今回の例だとイメージはこんな感じ👇
// まずこれがある
['3','-','1','+','2','*','-4','/','2']
// 2 * -4 を先に計算して
['3','-','1','+','-8','/','2']
// -8 / 2 も計算して
['3','-','1','+','-4']
applyMulDiv のコード(* / を優先計算する)
function applyMulDiv(tokens) {
const result = [];
let i = 0;
while (i < tokens.length) {
const t = tokens[i];
if (t === "*" || t === "/") {
const prev = Number(result.pop());
const next = Number(tokens[i + 1]);
let calc;
if (t === "*") {
calc = prev * next;
} else {
if (next === 0) {
display.value = "0では除算できません";
return null;
}
calc = prev / next;
}
result.push(String(calc));
i += 2; // 演算子と次の数をまとめて消費する
} else {
result.push(t); // 数字や + - はそのまま残す
i++;
}
}
return result;
}
処理の要約(めちゃ大事)📌
* or / が出てきたら
✅ 前の数(result.pop) と ✅ 次の数(tokens[i+1]) を取り出して計算
✅ 計算結果を result に入れる
✅ i += 2 して 次の数までまとめて消費
それ以外(数字や + -)は
✅ そのまま result に入れる
✅ i++
例:['3','-','1','+','2','*','-4','/','2'] を処理してみる
最初に * が出てくるのはここ👇
2 * -4
この時点での配列はこうなっています:
result = ['3','-','1','+','2']
tokens = ['3','-','1','+','2','*','-4','/','2']
i = 5 // tokens[5] が "*"
✅ 前後の数を取り出す
prev = Number(result.pop()); // 2
next = Number(tokens[i + 1]); // -4
✅ 計算して result に戻す
calc = prev * next; // 2 * -4 = -8
result.push(String(calc));
ここまでで result は👇
result = ['3','-','1','+','-8']
そして i += 2 なので、
*と -4 はまとめて消費され、次は / の位置へ進みます。
次の / を処理する
今度は、-8 / 2となるので、
prev = -8
next = 2
calc = -4
結果、applyMulDiv の返り値はこう👇
['3','-','1','+','-4']
🎉 これで「足し算・引き算だけの配列」になりました!
② = 押下時に + - を計算して表示する 🟰
次は equal() の中で、
表示(× ÷)を内部用(* /)に変換して、tokens を作ります。
-
mergeUnaryMinus で -4 を1要素にする
-
applyMulDiv で * / を先に計算
-
残った + - を左から順に計算
-
表示する
という流れになります!
equal の中身(+ - の計算部分)
let result = Number(tokens[0]);
for (let i = 1; i < tokens.length; i += 2) {
const op = tokens[i];
const num = Number(tokens[i + 1]);
if (op === "+") {
result = result + num;
} else if (op === "-") {
result = result - num;
}
}
ここでのポイントは👇
tokens は ['数', '演算子', '数', '演算子', '数', ...] の形
なので i += 2 で 演算子の位置だけ見ればOKです。
例:['3','-','1','+','-4'] を計算する
最初に
result = 3
そこから順番に処理します👇
3 - 1 = 2
2 + (-4) = -2
結果は -2 になります 🎉
小数点の誤差対策(丸め処理)🔧
割り算や小数は、JavaScriptの仕様上誤差が出ることがあります。
そこで最後に、小数点を丸めて誤差を抑えます。
result = Math.round(result * 1000000000) / 1000000000;
小数点第9〜10位あたりで丸めることで、
例えば 0.1 + 0.2 問題みたいな誤差を軽減できます。
エラー制御 🚧
電卓は「どんな入力でも壊れない」ことが大切なので、以下も入れています。
-
何も入力せず = → 何もしない
-
「0では除算できません」表示中に = → 0に戻す
-
末尾が演算子(例:3+ / 3-)で = → 何もしない(値保持)
-
関数の返り値が null のとき → 後続を止める
このあたりを入れておくと、実際の電卓っぽさが一気に上がります 👍
✅ まとめ
今回やったことはこの2つです!
(1) * / を先に計算して tokens を整理する
(2) 残った + - を左から順に計算して結果を出す
これで = を押したときに計算結果が出る、
本格的な電卓アプリが完成しました 🎉
あとがき ✍️
電卓って「ボタン押したら計算するだけでしょ?」と思いきや、
実際に作ってみると 入力制御・例外処理・優先順位 が山ほどあって、めちゃくちゃ勉強になりました。
特に、
-(マイナス) を符号として扱う
演算子が連続したときの挙動
*(かける) /(わる) を優先して計算するための配列処理
この辺りを自分で書けるようになると、
文字列処理・配列処理・条件分岐の理解が一気に深まる感覚がありました。
ここまで読んでいただきありがとうございました!🙌
「ここもっと詳しく!」や「こういう入力も対応したい!」などあれば、コメントでもDM頂ければ励みになりますので、ぜひよろしくお願いします![]()
