【eval不要】JavaScriptで電卓を0から作ってみた

JavaScriptで電卓を作ってみたい。

作り方にはいくつか方法があります。

ただし次のチート的手法は使いません。

電卓UIの実装から数式の構文解析まで、
JavaScriptで0から電卓を作ることにします。

そういうチャレンジングな内容です。

作成する電卓の仕様について

次のような仕様で電卓を作ります。

  • 独自の数式構文解析機構を持つ
  • 数字は0~9の整数で少数は扱わない
  • 演算子は + - × ÷ の4種類
  • 括弧 () による数式記述にも対応
  • ついでにAC・DELも実装する

ここでは整数のみを受け付けることにします。

もちろん小数対応することも可能だけど、電卓入門ということで省きました。小数に対応できないようなコード構成ではないので安心してください。

UI・計算機構も含めて全部自力で作ります。

1.電卓UIをグリッドレイアウトで作成

手始めに電卓UIから作っていきましょう。

電卓はボタンが等間隔で規則的に並んでます。

そういうUIにはグリッドレイアウトが最適です。

▼ CSSのグリッドレイアウトとは何か

グリッドは、列と行を定義する水平線と垂直線の集合が交差したものです。要素をグリッド上の行と列の中に配置することができます。 CSS グリッドレイアウトには次のような特徴があります。

引用元 : https://developer.mozilla.org/ja/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout

▼ グリッドレイアウトの基本概念

グリッドの仕様はMDNなどで確認してください。

 

ここではグリッドレイアウトをメインで使います。

次が電卓の装飾なしのスケルトンUIです。

▼ 次のようなHTML

▼ 必要最低限のCSS

▼ このような電卓が表示できる

グリッドレイアウトで作った電卓の必要最小限のレイアウト

短いコードで電卓UIが作れました。

もし今までのフレックスレイアウトを使ったら、思い通りの配置にするのは難しいです。特にボタン0の横幅など、柔軟なレイアウトができるのがグリッドの強みですね。

最低限のUIはこれだけです。

2.ボタンに装飾やアニメーションを付与

次に大雑把だけど装飾をつけてみます。

角を丸くしたり、押下時のアニメなどです。

▼ 次のようなCSSを追加

▼ 最終的な電卓UIの見た目

電卓にボタンの装飾やクリック時のアニメーションを付与した

電卓らしさが一気に増しました。

レイアウト実装は以上で完了です。

3.数式の構文解析機構を実装する

ここからが電卓の根幹にかかわる部分です。
数式を構文解析するパーサーが必要になります。

用いるのは次の手法です。

再帰下降構文解析

難しそうな名前だけど、やることは簡単。再帰関数を使って構文を小さな単位まで分割していき、その末端から値を確定していく手法です。

参考にしたのは次の記事です。

▼ 構文解析 - アルゴリズム講習会

▼ 四則演算での構文解析のイメージ

ここでは四則演算は数字と括弧、+-*/の4つの演算子から成り立っているとします。演算子の優先順位も実際の四則演算の通り、掛け算と割り算が優先されます。ただし、全ての演算は整数だとします。以下は式の一例です。

1+2*6/(10-7)

まずは、四則演算の構文をBNF記法で表します。 BNF記法をあまり厳格に記述する必要はありませんが、演算子の優先順位はきっちり判別できるようにしておく必要があります。

最初に、式全体をexprという変数(非終端記号)で表すとします。 exprの中から計算の優先順位を低い順にterm, factor, numberという記号を対応付けると、次のようなBNF記法を作ることができます。 (exprやtermという名前は適当に決めたものです)

<expr> ::= <term> [ ('+'|'-') <term> ]*
<term> ::= <factor> [ ('*'|'/') <factor> ]*
<factor> ::= <number> | '(' <expr> ')'
<number> :== 1つ以上の数字

このBNF表記での[...]*という表記は、括弧内のものが0回以上繰り返されることを意味します。

引用元 : https://dai1741.github.io/maximum-algo-2012/docs/parsing/

ここにC言語での実装方法が載っていました。

それをJavaScript用に書き直します。

 

再帰下降構文解析を使ったパーサー、

それは以下のようなコードで実装できます。

▼ 数式を解析して値を返すパーサー

再帰的に数式を最小パーツに分けて解析してます。

最小単位は数字または括弧で囲まれた数式です。最小単位(数字 or 括弧) ⇒ かけ算割り算 ⇒ 足し算引き算 とさかのぼって解析が行われます。

この手法の利点は複雑な数式をシンプルなロジックで処理でき、括弧などの入れ子構造も容易に解析できる点です。また新しい演算子も再帰関数を追加すれば事足ります

電卓の計算機構が完成しました。

4.電卓でボタン入力を受け取り結果を表示

最後にUIと機能を結びつけます。

ボタン入力を受け取り、計算結果の表示です。

▼ このようなコード

数式の構文通りに入力できるようにボタン入力を制御します。たとえば演算子は数字または閉じ括弧の前にしか置けなかったり、括弧はかならず演算子前のみ入力可能としたり…

それから = を押したときの挙動についてです。

数式解析パーサーに数式を渡し、その結果をデイスプレイに表示してます。簡易的な電卓なので答え表示と数式表示は分離されていません。

各自で工夫してください。

電卓を動かしてみた(Gif動画)

実際に電卓をブラウザで動かしてみます。

▼ 簡単な計算をしてみた

JavaScriptで作った電卓をブラウザで動かしている様子。eval関数なし、ライブラリもなしで実装できた

UIから計算機構まで全て自力で作れました。

安易にライブラリを使ったり、セキュリティ度外視でevalを使うこともできたけど、電卓を0から作る楽しさはそこから得られないと思います。

以上、JavaScriptで電卓を作るでした。

質問などはコメント欄に書いてください。ではまた